'개발 & SDK'에 해당되는 글 15건

  1. 2010/11/10 터칭 아이폰 2장을 새롭게 업데이트 했습니다. (4)
  2. 2010/10/20 예제를 최신 SDK에서 컴파일 되도록 업데이트 했습니다. (1)
  3. 2010/10/04 책 관련 문의사항은 메일을 주시기 바랍니다.
  4. 2009/06/19 Clang으로 코드 오류 찾기 (1)
  5. 2009/06/16 iPhone SDK 메모리 관리 기법 (2)
  6. 2009/06/15 쓰레드 기초
  7. 2009/06/12 키-값 코딩과 키-값 감시(Key-Value Coding/Key-Value Observing)
  8. 2009/06/10 프로퍼티와 접근자(Accessor)
  9. 2009/06/08 Notification (통보) 사용하기 (3)
  10. 2009/04/26 iPhone 어플리케이션의 실행 구조

터칭 아이폰 2장을 새롭게 업데이트 했습니다.

개발 & SDK 2010/11/10 21:00

안녕하세요. 책의 도입부인 2장에서 툴 업데이트로 인해 최신 iOS SDK에서 예제를 따라 할 수 없는 상황입니다.
책의 2장을 새로운 SDK에 맞게 수정해서 원문으로 공개합니다. 되도록 많은 분들이 이 글을 보고, 업데이트 된
내용을 통해서 책을 보시면 좋겠습니다.

출판물의 특성상 이미 출판된 책의 내용을 고칠 길은 없습니다. 그 동안의 혼란에 죄송하다는 말씀을 드립니다.
아래 링크 외에도 인사이트 출판사의 스프링 노트에도 업데이트 할 예정입니다.

* 공개된 2장은 아직 최종 교정을 거치지 않았고, 필자가 임의로 편집한 것입니다. 양해부탁드립니다.


저작자 표시 비영리 변경 금지
Creative Commons License
Trackback 0 : Comments 4

Trackback Address :: http://maclove.pe.kr/trackback/39 관련글 쓰기

  1. 2012/03/15 14:48 Modify/Delete Reply

    비밀댓글입니다

  2. 2012/03/21 18:22 Modify/Delete Reply

    비밀댓글입니다

  3. 2012/03/27 16:18 Modify/Delete Reply

    비밀댓글입니다

  4. 2012/04/05 18:08 Modify/Delete Reply

    비밀댓글입니다

Write a comment


예제를 최신 SDK에서 컴파일 되도록 업데이트 했습니다.

개발 & SDK 2010/10/20 03:21

예제는 다음의 구글 코드 사이트에서 관리되고 있습니다. 수정되는 사항은 항상 이곳에 반영되니, 그때 그때 업데이트 하시면 되겠습니다.

http://code.google.com/p/touching-iphone/

우선 본인의 맥에서 소스를 다운받기 위해서는 다음 그림과 같이 Terminal을 실행시킨 후 다음 명령을 입력하면 됩니다.

> mkdir example            <-- 저장하고자 하는 디렉터리를 하나 생성함
> cd example                <-- 해당 디렉터리로 이동
> svn checkout http://touching-iphone.googlecode.com/svn/trunk/ touching-iphone-read-only

감사합니다.

기존의 압축된(zip) 예제 링크도 새롭게 업데이트 하였습니다.

저작자 표시 비영리 변경 금지
Creative Commons License
Trackback 0 : Comment 1

Trackback Address :: http://maclove.pe.kr/trackback/38 관련글 쓰기

  1. 지나가다 2010/10/20 10:42 Modify/Delete Reply

    출간후에도 지속적으로 관리해주셔서 감사드립니다.. __);
    고급편도 발간하신다고 하셨는데.. 언제쯤될지 궁금하네요..
    얼른 출간되기를 기대하고 있습니다... ㅎㅎㅎ

Write a comment


책 관련 문의사항은 메일을 주시기 바랍니다.

개발 & SDK 2010/10/04 11:30

안녕하세요. 많은 독자분들이 책에 대해서 궁금한 점이 있을것이라고 생각됩니다.
책이 나오고 거의 바로 새롭게 아이폰 SDK가 버젼없이 되면서 상당한 혼란이 있었습니다.
기본적으로 책이 쓰여지는 시점에 사용했던 SDK와 iOS를 기준으로 책이 쓰여져 있기 때문에,
현재 사용하시는 iOS와 SDK와 다른 부분이 있을 수 있습니다. 책을 쓰는 시점에서 모든 예제를
직접 만들어서 정상적으로 돌아가는지 전부 두번씩 확인을 하였습니다.

혹시 예제가 동작되지 않거나, 기타 책과 관련된 문의사항이 있으시면 저에게 메일을 보내주시기 바랍니다.
fromdj2k@gmail.com 입니다. 감사합니다.
저작자 표시 비영리 변경 금지
Creative Commons License
Trackback 0 : Comment 0

Trackback Address :: http://maclove.pe.kr/trackback/37 관련글 쓰기

Write a comment


Clang으로 코드 오류 찾기

개발 & SDK 2009/06/19 19:09

CLang 정적 분석기

CLang 정적 분석기는 Objective-C와 C언어로 작성된 코드를 분석해서 오류를 발견해 주는 툴이다. 디버깅이나 인스투루먼츠는 실행중인 프로그램을 분석하는 동적인 작업인 반면, CLang은 정적으로 소스 코드 자체를 분석해서 오류를 검사한다. 검사할 수 있는 오류는 메모리의 누수, dealloc누락, 널(Null)값 참조 등이 있다. 직접 사용해 보자.(CLang은 또다른 프로젝트 LLVM 컴파일러를 위한 프론트엔트 프로젝트이다. LLVM은 GCC를 기반으로 컴파일 성능을 최적화하는 기술을 적용한 프로젝트이다. CLang 프로젝트의 일부로 CLang 정적 분석기 프로젝트가 포함된다. )

*프로젝트 설정하기

CLang분석기를 이용하기 위해서 Xcode상에서 설정할 사항이 하나 있다.

Base SDK 설정

위 그림과 같이 프로젝트 속성에서 Base SDK를 iPhone Simulator로 설정해야 한다. iPhone Device의 경우는 코드 서명 문제로 오류가 발생하기 때문이다. 우리는 정적으로 코드만 분석하면 되기 때문에 시뮬레이터를 선택해 주자.

* 설치하기

CLang 정적 분석기는 다음 사이트에서 무료로 다운로드 받을 수 있다. 오픈소스 프로젝트 이기 때문에 소스코드도 받을 수 있다. 아래 링크에서는 친절하게 맥OS를 위해서 바이너리 형태로 파일을 제공해 주기 때문에, 바로 쓸 수 있다.

http://clang-analyzer.llvm.org/installation.html

다운로드 받은 파일의 압축을 풀면 checker-0.211 과 같이 check-버젼에 해당하는 디렉터리가 생긴다. 이 디렉터리를 원하는 위치에 위치시키자. 그리고 경로에 그 위치를 추가한다. 터미널을 실행 시키고, 다음과 같이 PATH에 경로를 추가하자.

$ export PATH=$PATH:/디렉터리 위치/checker-0.211

* 분석 진행

이제 Xcode 프로젝트가 있는 디렉터리로 이동하자. 그리고 다음 명령을 실행시키자.

$ xcodebuild clean -configuration Debug
$ scan-build -k -V xcodebuild -configuration Debug -sdk iphonesimulator3.0

첫 번째 명령은 프로젝트의 빌드파일을 모두 정리하는 명령이다. 두번째가 실제로 CLang분석기를 실행시키는 명령이다. 명령을 내리면 CLang분석기는 디버그 타깃을 시뮬레이터SDK로 빌드하고, 그 결과를 보여줄 것이다. 문제가 발견되면 그림30과 같이 웹페이지를 실행시켜 준다. (만약 프로젝트의 빌드가 실패하면 분석결과 오류를 발견했더라도 실행이 멈추게 되어있다. 성공적으로 빌드되었는지 확인하기 바란다.) 만약 성공적으로 빌드하고 오류가 전혀 발견되지 않았다면 웹페이지는 따로 보여주지 않는다.

CLang 분석기로 애플리케이션을 하나 분석해 보자. 아래 그림이 그 결과다.

<그림> 발견한 오류를 웹 페이지로 보여준다.

다행이 오류가 하나 만 발견되었다. Leak of returne object 오류인데, 객체를 해제하지 않았다는 오류이다. 한 가지 주의할 점은 CLang 분석기가 오류로 판단했다고 해서 반드시 오류는 아니라는 것이다. 어디까지나 그 것이 오류인지를 판단하는 것은 개발자의 몫이다. 진짜 오류인지 한번 들어가 보자. 왼쪽 화면의 View Report 를 클릭하자. 그러면 문제가 되는 소스코드를 보여주고, 친절하게 설명까지 보여준다.

31행에서 NSURL 객체를 생성하고 34행에서 잘 사용했다. 그런데 해제하지 않았다. 실수다. 고치도록 하자.

25: - (void)viewDidLoad {
26: [super viewDidLoad];
27:
28: id sndPath = [[NSBundle mainBundle] pathForResource:@"dog"
29: ofType:@"aiff" inDirectory:@"/"];
30: // URL 타입 생성
31: CFURLRef baseURL = (CFURLRef)[[NSURL alloc]
32: initFileURLWithPath:sndPath];
33: // SoundID 생성
34: AudioServicesCreateSystemSoundID(baseURL,&m_sndID);
35: CFRelease(baseURL);
36: }

어떤가? 상당이 좋은 툴이다. 자신이 개발하고 있는 프로젝트도 당장 CLang분석기로 분석 해보자. 큰 도움이 될 것이다.

Creative Commons License
Trackback 0 : Comment 1

Trackback Address :: http://maclove.pe.kr/trackback/33 관련글 쓰기

  1. 윤광근 2010/07/20 16:41 Modify/Delete Reply

    예제파일을 열려고하는데요.. 속성창중에 base sdk를 바꿀수잇는 속성이없는거같은데...

    이거어떻게 해야할까요?;;

    xcode 3.2.3 버전이라고 나오네요...

Write a comment


iPhone SDK 메모리 관리 기법

개발 & SDK 2009/06/16 13:18

메모리 관리

많은 개발자들이 Objective-C 의 메모리 관리를 어렵게 느낀다. Objective-C 에서 객체는 참조 카운터를 이용해서 관리되기 때문에, 참조 카운터를 증가 시키고 감소하는 작업이 프로그래머의 몫이 된다. 여기서 혼돈이 오는 것이다. 참조 카운터를 증가시키는 함수는 retain 이다. 반대로 참조 카운터를 감소시키는 함수는 release와 autorelease 이다. 하지만 문제는 이미 만들어진 객체의 메서드가 내부에서 retain 을 몇번 부르는지를 별도로 알아낼 방법이 없다는 것이다. 다음을 보자.

UIImageView* imgView = [[UIImageView alloc] init];

이 코드에서 UIImageView 는 retain 내부에서 한번 호출해서 객체를 반환해 준다. 그렇기 때문에 우리는 더이상 필요하지 않을때,

[imgView release];

를 호출해야 한다. 하지만 다음을 보자.

NSString* output = [NSString stringWithFormat:@”My name : %@”, name];

이 코드는 NSString의 stringWithFormat 클래스 메서드를 이용해서 객체를 생성하는 코드이다. 이 녀석은 retain 이 아닌 autorelease 를 호출해서 객체를 반환한다. 이렇게 어떤 메서드는 객체를 retain 해서 반환하고, 또 어떤 객체는 autorelease 로 반환하기 때문에 혼돈이 온다.

참조 카운트를 관리하는 함수는 다음과 같다.

• retain
     - 참조 카운트를 1 증가 시킨다.
• release
     - 참조 카운트를 1 감소시킨다. 참조 카운트가 0이 되면 메모리가 해제된다.
• autorelease
     -  객체를 오토릴리즈 풀에 등록한다. 나중에 오토릴리즈 풀이 제거될때 release 를 호출해 준다.

객체의 참조를 관리하는 것은 위 3개의 메서드가 전부이지만, 문제는 클래스 내부 구현에서 위 함수들을 호출하는 지를 모른다는 점이다. 하지만 법칙이 있다. 애플이 작성한 모든 클래스는 정해진 규칙을 철저하게 따르기 때문에 이 법칙들만 알아두면 더 이상 햇갈릴 일이 없다.

* 규칙1 : alloc, copy, new를 이름에 포함하는 메서드는 retain 된 객체를 반환한다.

alloc, copy, new 가 메서드 이름에 포함되어 있다면, 그 때 반환되는 객체는 retain 되어 있다. 그래서 반드시 release 를 직접 호출해야 한다.

01: queue = [[NSOperationQueue alloc] init];
02: // 작업
03: [queue release];
04:
05: copyedObj = [name copy];
06: // 작업
07: [copyedObj release];

* 규칙2 : 결자해지

만약 자신이 retain 을 호출했다면, 책임지고 release 또는 autorelease 를 호출해 주자.

01: [objA retain];
02: // 작업
03: [objA release];

반드시 자신이 호출한 retain 에 대해서 release/autorelease 짝을 맞추도록 하자.

* 규칙3: 클래스 메서드가 객체를 생성해 줄때는 autorelease 객체를 반환한다.

클래스 메서드로 객체를 생성하는 경우 즉 stringWithFormat 과 같은 클래스 메서드가 반환하는 객체는 모두 autorelease 객체이다.

01: img = [UIImage imageNamed:@"test.png"];
02: imgView = [[UIImageView alloc] initWithImage:];
03: // 작업
04: [imgView release];

코드 1행의 imageNamed 메서드 처럼 클래스 메서드를 이용해서 객체를 생성하는 경우 그 객체는 autorelease 가 호출된 객체이다. 그렇기 때문에 별도로 release 를 호출하지 않아도 오토릴리즈 풀이 자동으로 release 를 호출해 준다. 하지만 이 객체에 retain 을 호출 했다면 호출한 만큼의 release 를 호출 해야한다.

* 규칙4: 포함 객체는 추가된 객체를 retain 하고, 항목을 제거할 때 release 한다.

NSArray,NSDictionay 등의 포함 객체는 항목으로 추가되는 객체에 대해서 retain 을 한번 호출한다. 그리고 객체가 포함객체에서 제거될때 release 를 호출해 준다.

13.9.5 규칙5: retain 속성을 갖는 접근자

프로퍼티를 구성할때 @property (retain) ... 과 같이 retain 속성을 지정하면 이 프로퍼티에 객체를 설정하면 retain 메서드를 호출한다.

01: myImage = [[UIImage alloc] init]; // myImage 에 retain –> 1
02: self.userImage = myImage; // myImage 에 retain –> 2
03: [myImage release]; // myImage 에 release –> 1
04: // ...
05: self.userImage = myImage2; // 예전 myImage 에 release -> 0 해제

이와 같이 객체를 설정하는 접근자는 일반적으로 그 객체에 대해서 retain 을 호출한다. 그리고 만약 다른 객체를 재설정하면, 예전 객체에 release 를 호출한다. 위 예제의 3행에서 release 를 호출한 이유를 이해할 것이다.

*규칙5: 메서드가 반환하는 객체는 autorelease

메서드가 객체를 반환한다면 autorelease 된 객체를 반환하는 것이 좋다.

01: -(NSString*) findTopPlayer {
02: NSString *ret = [[top objectAtIndex:0] copy];
03: [ret autorelease];
04: return ret;
05: }

Creative Commons License
Trackback 1 : Comments 2

Trackback Address :: http://maclove.pe.kr/trackback/32 관련글 쓰기

  1. Tracked from geekinside's me2DAY 2010/03/01 21:32 DELETE

    Subject: fupfin의 생각

    네피림님의 Objective-C의 autorelease 이해하기 #1, #2. 그리고 강덕진님의 iPhone SDK 메모리 관리 기법.
  1. 개발자인간 2010/07/14 17:04 Modify/Delete Reply

    클래스 메서드 생성 객체 이외에는 수동 release가 반드시 필요하다고 보면 될까요?
    위 규칙3에서 [imgView release]; 가 굳이 필요없다는 말씀인걸로 이해됩니다만..

    어떻게 보면 C나 C++이랑 비슷하고, autorelease가 되니 자바랑 닮았고..
    오브젝트C는 참 재미있네요.. 경우에 따라 대응법이 다르니 유연하기도 하고..
    형식적이기도 하고.. 마치 문어같은 느낌입니다. - 맛도 좋은 문어~ ^^

  2. BlogIcon Glradios 2010/07/29 20:45 Modify/Delete Reply

    혼돈이라는 단어를 혼란의 의미로 쓰신 듯하네요!

Write a comment


쓰레드 기초

개발 & SDK 2009/06/15 02:18

아아폰에서 모든 어플리케이션은 기본적으로 하나의 쓰레드를 가지고 있다. 이 쓰레드는 UIApplication 클래스에서 자동으로 생성하며 프로그램의 메인 룹이 이곳에서 실행된다. 하지만 어떤 작업이 그 작업을 마치는데 너무 오랜 시간이 걸린다면 그 작업을 새로은 쓰레드로 만들어서 실행하는 것이 좋다. 메인 쓰레드는 기본적으로 사용자와 상호 작용을 해야하는데, 어떤 작업이 시간을 너무 소모하면 사용자가 프로그램이 멈춘 것으로 오해 할 수 있다.

아이폰 OS 에서 쓰레드는 리눅스와 유닉스에서 지원하는 POSIX 쓰레드를 그대로 이용할 수 있다. 또한 코코아 터치 프레임워크 자체에서도 NSThread 라는 클래스를 이용해서 쓰레드를 좀 더 편리하게 생성할 수 있게 해 준다. 만약 이미 POSIX 로 작성된 코드를 가지고 있는 경우라면 새롭게 NSThread로 포팅하지 않고 바로 사용하는 것이 좋을 것이다.

이제 부터 코코아 터치가 제공하는 쓰레드 기능을 하나씩 구현해 보도록하자. POSIX 쓰레드는 이책에서는 따로 설명하지 않겠다.

* 쓰레드 생성하기

먼저 NSThread 객체를 이용해서 쓰레드를 생성해 보자. 다음은 NSThread 의 클래스 메서드를 이용하는 방법이다.

01: [NSThread
02: detachNewThreadSelector:@selector(threadFunc:)
03: toTarget:self
04: withObject:nil
05: ];

detachNewThreadSelector 메서드는 지정한 객체의 메서드를 이용해서 새로운 쓰레드를 생성해 준다. 위 코드에서 생성된 쓰레드는 바로 실행을 시작한다. 다음은 쓰레드를 생성하는 다른 방법이다.

01: NSThread* myThread = [[NSThread alloc]
02: initWithTarget:self
03: selector:@selector(threadFunc:)
04: object:nil
05: ];
06: [myThread start];

위 코드는 alloc, init 으로 생성을 먼저 한 후에 실행은 나중에 start 메서드를 호출할때 하도록 할 수 있다.

마지막으로 NSObject 를 이용해서 쓰레드를 생성하는 방법도 있다.

01: [objectA
02: performSelectorInBackground:@selector(doSomething)
03: withObject:myName];

 

* 쓰레드 메서드 구현하기

쓰레드에서 실행될 메서드는 다음과 같이 정의한다. 전달되는 인자는 쓰레드를 생성할 때 설정했던 object 가 넘어온다.

01: - (void)doSomething:(NSString*)name {
02: NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
03:
04: // 작업
05:
06: [pool release];
07: }

오토릴리즈(autoreleae) 풀은 객체 중에서 autorelease 메서드를 호출해서 생성된 객체들을 관리한다. 오토릴리즈 풀을 생성하면 그 이후로 생성되는 모든 autorelease 객체를 마지막에 생성된 오토릴리즈 풀 객체가 관리한다. 그리고 오토릴리즈 풀을 해제(release) 하면 이때 모든 오토릴리즈 객체도 같이 해제된다.

모든 쓰레드는 자신만의 오토릴리즈 스택을 관리하기 때문에, 쓰레드를 생성한 후 반드시 오토릴리즈 풀을 생성해야 한다.

* 런 루프

런 루프는 쓰레드가 외부와 통신하기 위해서 존재한다. 런 루프는 자신(쓰레드)에게 전달된 이벤트가 있는지를 검사하고, 만약 존재하는 경우 알맞은 메서드를 호출해 준다. 런 루프가 처리하는 이벤트는 크게 두가지가 있다.

• 입력 소스
    • performSelector:: 계열의 메서드를 호출해서 지정한 셀렉터를 실행시켜달라는 외부의 요청.
• 타이머
    • 타이머를 생성하면 그 타이머는 코드가 실행된 쓰레드의 런 루프에 소속된다. 런 루프는 타이머중에서 지정한 시간이 경과한 것들을 검사해서 메서드를 호출해 준다.

다음 코드를 보자.

01: if ( tapCount == 2) {
02: // 5개 모두 보여주자.
03: [self performSelector:@selector(showAllCircles) withObject:nil afterDelay:0.3];
04: NSLog(@"Scheduled");
05: }
06: ... 생략 ...
07: if (tapCount > 2) {
08: [UIView cancelPreviousPerformRequestsWithTarget:self];
09: NSLog(@"Cancelled");
10: }

이때 이미 쓰레드에 입력 소스를 추가하는 작업을 구현한 것이다. performSelector:withObject:afterDelay 메서드를 호출하면 현재 쓰레드의 런 루프에 입력 소스로써 등록이 된다. 그리고 런 루프가 실행될 때 자신에게 등록된 모든 입력 소스를 검사해서 조건이 만족되는 (여기서는 0.3초 경과) 경우 해당 메서드를 호출(예제에서는 showAllCircles)해 주는 것이다.

만약 스스로 쓰레드를 생성했다면 런 루프가 자동으로 실행되지는 않는다. 그래서 자신의 쓰레드 함수에서 직접 런 루프를 실행시켜서 주기적으로 외부의 이벤트에 대응할 수 있도록 만들어야 한다. 하지만 쓰레드가 하나의 작업만 수행하고 종료되는 경우라면 반드시 필요하지는 않다. 하지만 무한 루프를 돌면서 지속적으로 상호작용이 필요한 쓰레드라면 반드시 런 루프를 실행해 주도록 하자.

01: - (void)doSomething:(NSString*)name {
02: NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
03:
04: NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
05: BOOL stop=NO;
06:
07: while(!stop) {
08: // 작업
09: [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
10: }
11:
12: [pool release];
13: }

4행에서 현재 쓰레드의 런 루프를 얻어온다. 그리고 9행에서 런 루프를 실행시킨다. 이 때 runIntilDate 메서드는 지정한 시간까지 런 루프를 실행시켜 준다. 하지만 런 루프가 아무런 할 일이 없다면 바로 종료된다.

* 쓰레드의 대안

쓰레드를 생성하는 코드를 직접 작성하지 않고도 간접적으로 쓰레드를 생성하게 할 수 있다. 이때 NSInvocationOperation 객체를 이용한다.

01: NSInvocationOperation* op =
02: [[[NSInvocationOperation alloc]
03: initWithTarget:self
04: selector:@selector(myMethod:) object:data] autorelease];

위 코드는 NSInvocationOperation 객체를 생성해 준다. 다음으로 할 일은 동작을 시작시키는 일이다.

01: NSOperationQueue* queue = [[NSOperationQueue alloc] init];
02: [queue setMaxConcurrentOperationCount:1];
03: [queue addOperation:op];

위 코드와 같이 NSOperatonQueue 객체를 생성하고 그 객체에 작업을 등록해 주면 된다.

Creative Commons License
Trackback 0 : Comment 0

Trackback Address :: http://maclove.pe.kr/trackback/30 관련글 쓰기

Write a comment


키-값 코딩과 키-값 감시(Key-Value Coding/Key-Value Observing)

개발 & SDK 2009/06/12 01:22

* 키,값 코딩 (Key-Value Coding)

키 값 코딩은 NSObject 를 상속받는 모든 객체가 지원한다. 이는 NSKeyValueCoding 라는 프로토콜을 NSObject 가 따르고 있기 때문이다. 키 값 코딩은 무엇일까 ?

키 값 코딩 기술은 객체의 속성을 문자열을 통해서 간접적으로 접근하기 위한 것이다. 즉 객체의 속성에 접근하기 위해서 직접 객체의 접근자를 호출할 것이 아니라 속성의 이름을 인자로 전달해서 그 값에 접근하는 것이다. 객체에 접근할 때 내부적으로 접근자를 이용하게 된다. 만약 접근자가 존재하지 않으면 직접 멤버 변수에 접근한다. 다음 예제 코드를 보자.

01: - (void)applicationDidFinishLaunching:(UIApplication *)application {
02:
03: imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"test.png"]];
04: [window addSubview:imgView];
05: [window makeKeyAndVisible];
06:
07: //imgView.frame = CGRectMake(0,0,320,480);
08: [imgView setValue:[NSValue valueWithCGRect:CGRectMake(0,0,320,480)] forKey:@"frame"];
09:
10: //[imgView setAlpha:0.5];
11: [imgView setValue:[NSNumber numberWithFloat:0.5] forKey:@"alpha"];
12:
13: //imgView.superview.backgroundColor = [UIColor yellowColor];
14: [imgView setValue:[UIColor yellowColor] forKeyPath:@"superview.backgroundColor"];
15:

먼저 7,8 행을 보자. imgView 는 이미지 뷰의 객체이다. 뷰의 frame 속성을 변경하는 기존 방법은 7행과 같이 점 표현법으로 접근하거나, [imgView setFrame:] 과 같이 접근자를 호출하는 것이었다. 하지만 예제는 setValue:forKey 메서드를 이용해서 문자열로 속성값에 접근한다. 이러한 접근법을 키-값 코딩이라고 한다.

13,14행은 키-값에 대해서 경로를 사용한 것이다. 키-값 경로는 접근하려는 속성에 대해서 점 표현법 문자열을 쓰게 되면, 그 문자열을 분석해서 재귀적으로 키-값 코딩을 적용하는 것이다.

14행을 보면 imgView 의 superview 속성에 키-값 코딩으로 접근하고, 그 superview 객체에서 다시 키-값 코딩으로 backgroundColor 속성에 접근한다. 이와 같이 키-값 코딩과 키-값 경로를 이용한 방법은 점 표현법, 접근자와 밀접한 관계를 갖는다.

값을 읽는 방법도 마찬가지로 간단하다.

16: // 값 읽기
17: UIImage* image = [imgView valueForKey:@"image"];
18: UIColor* superBack = [imgView valueForKeyPath:@"superview.backgroundColor"];
19: NSLog(@"%@ %@",image,superBack);
20: }

코드의 17,18행에서 보는 것과 같이 valueForKey, valueForKeyPath 메서드를 이용해서 접근한다. 점 표현법을 사용하는 문자열로 값에 접근할 때는 반드시 valueForKeyPath 를 이용하는 것에 주의하자.

그렇다면 NSObject 가 키-값을 검색하는 방식은 어떻게 될까 ? NSObject 는 키-값 코딩으로 접근하는 메서드가 호출되면 그 키와 일치하는 이름의 접근자를 검색한다. 만약 접근자가 존재하지 않으면 직접 객체에서 일치하는 멤버 변수를 검색한다.

* 예외 상황

키-값 코딩으로 값을 읽으려고 할 때 키 값이 존재하지 않는 경우가 있다. 이럴때는 객체의 valueForUnderfinedKey 메서드가 호출된다. 이 메서드는 기본적으로 NSUndefinedKeyException 예외를 발생시킨다. 만약 존재하지 않는 키를 접근하려고 할 때 자신만의 동작을 정의하고 싶다면 valueForUndefinedKey 메서드를 재정의 하면 된다.

마찬가지로 값을 쓰는 경우도 해당 키가 존재하지 않는다면 setValue:forUndefinedKey: 메서드가 호출된다. 이 메서드도 기본 구현은 NSUndefinedKeyException 예외를 발생시킨다. 예외 대신에 별도의 작업을 진행하려면 setValue:forUndefinedKey 를 재정의 하자.

* 키-값 감시 (Key-Value Observing)

키-값 감시 기능은 객체의 프로퍼티가 변경되는 상황을 감시할 수 있다. 통보와 비슷하게 자신이 감시하고 싶은 키 경로를 지정해서 옵져버로 등록해 두면, 해당 키 경로의 값이 수정되었을 때 메서드를 호출해 준다. 키 경로를 감시하는 코드는 다음과 같다.

01: [objectA addObserver:self
02: forKeyPath:@"enable"
03: options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
04: context:NULL];

위 코드는 objectA 의 enable 이라는 프로퍼티 (키 경로)가 변경될 때를 감시하기 위한 것이다. 감시하는 객체는 self 이다. 그리고 3행에서는 옵션을 지정하게 되는데, 값이 변경되었다는 메서드가 호출될 때 딕셔너리로 전달될 정보를 결정한다. 마지막 4행의 context 는 값이 변경되었다는 메서드를 호출할때 같이 전달된다. 추가 정보를 인자로 전달할 때 사용한다.

위와 같이 코드를 수행하고, 코드의 어디선가 아래와 같이 속성을 변경한다고 하자.

[test setValue:[NSNumber numberWithBool:YES] forKey:@"enable"];

또는

test.enable = YES;

이렇게 키-값 코딩 이나 점 표현법을 이용해서 객체의 속성값을 변경하면 그 속성을 감시하고 있는 객체들의 다음 메서드를 호출해 준다.

01: - (void)observeValueForKeyPath:(NSString *)keyPath
02: ofObject:(id)object
03: change:(NSDictionary *)change
04: context:(void *)context
05: {
06: if ([keyPath isEqual:@"enable"]) {
07: for (NSString* val in [change allKeys]) {
08: NSLog(@"%@ : %@",val,[change objectForKey:val]);
09: }
10: }
11: }

전달되는 인자를 보자. 1행은 키 경로가 전달된다. 예제의 경우 “enable” 이 된다. 2행은 값이 변경된 객체가 전달된다. 그리고 4행은 감시자를 등록할 때 지정했던 context 값이 그대로 넘어온다. 이제 3행을 보자. 3행은 변경된 값에 대한 정보를 딕셔너리 형태로 담고 있다. 이 딕셔너리에 포함되는 정보는 다음과 같다.

• NSKeyValueChangeKindKey
    • 값이 변경된 형태를 알려준다.
• NSKeyValueChangeNewKey
    • 새로운 값
• NSKeyValueChangeOldKey
    • 변경되기 전의 값

마지막으로 키-값 감시를 더이상 하지 않을 때는 제거해야 한다. 다음과 같이 간단하게 제거할 수 있다.

01: - (void)unregisterForChangeNotification
02: {
03: [objectA removeObserver:self forKeyPath:@"enable"];
04: }

* 수동으로 알리기

지금까지 배운 것처럼, 어떤 속성에 대해서 접근자(점 표현법이나 키-값 코딩) 를 이용해서 값을 변경하면 그 내용이 자동으로 감시자에게 통보된다. 하지만 객체의 내부에서 수동으로 통보할 수도 있다.

01:- (void) foo {
02: [self willChangeValueForKey:@"enable"];
03: _enable = YES;
04: ...
05: [self didChangeValueForKey:@"enable"];
06: }

2행과 5행에서 처럼 값이 곧 변경될것이다, 값이 변경되었다는 것을 알리기 위한 메서드를 호출해 주면 된다.

Creative Commons License
tags : KVO KVC
Trackback 0 : Comment 0

Trackback Address :: http://maclove.pe.kr/trackback/29 관련글 쓰기

Write a comment


프로퍼티와 접근자(Accessor)

개발 & SDK 2009/06/10 01:16

접근자는 클래스의 멤버 변수에 접근하기 위한 함수들을 말한다. 클래스를 구현하다 보면 멤버 변수의 값을 읽고, 설정하는 작업이 매우 빈번하다. 객체지향 프로그래밍에서는 데이터 캡슐화(Encapsulation)를 위해서 클래스의 내부 변수를 직접 접근하는 대신 접근자를 이용하도록 하고 있다. 다음을 보자.

01: @interface AccessorTest : NSObject {
02:
03: BOOL enable;
04: }
05:
06: -(BOOL) enable;
07: -(void) setEnable:(BOOL)en;
08: @end

enable 이라는 멤버 변수를 접근하기 위한 접근자를 선언했다. 이와 같이 값을 읽기 위한 접근자는 그 변수 이름을 그대로 이용한다. 반대로 값을 설정하기 위한 접근자는 변수 이름의 첫글자를 대분자로 바꾸고 그 앞에 set 을 더한 이름을 사용한다. 이렇게 접근자를 선언하고 구현하면 오브젝트 C의 다른 혜택들을 자동으로 누릴 수 있다. 바로 점 표현법과 키-값 코딩이다. 다음 구문을 보자.

01: theObject.enable = YES;

위 표현이 점 표현법이다. 그런데 이 코드는 사실 다음과 같이 변환되어 처리된다.

01: [theObject setEnable:YES];

그렇다 enable 이라는 이름으로 점 표현을 사용하면 setEnable 라는 이름의 접근자를 호출하도록 미리 약속되어 있는 것이다. 이 내용은 13.3절에서 배울 키,값 코딩에도 똑같이 적용된다.

접근자를 구현한 부분을 보자.

01: @implementation AccessorTest
02:
03: -(BOOL) enable {
04: return enable;
05: }
06: -(void) setEnable:(BOOL)en {
07: enable = en;
08: }
09:
10: @end

단순하게 값을 설정하고, 현재 값을 반환하는 일이 전부다. 이처럼 접근자는 아주 편리하지만, 실상 접근자를 구현하는 작업은 거의 비슷한 코드를 반복적으로 작성하는 일이 대부분이다. 그래서 고안된 것이 프로퍼티이다. 프로퍼티는 접근자를 자동으로 생성해 준다. 프로퍼티를 정의할 때 접근자에 대한 속성을 설정해서 접근자가 구현되는 방식을 결정할 수 있다. 위 코드는 다음과 같이 대체가 가능하다.

01: @interface AccessorTest : NSObject {
02: UIImage* img;
03: NSString* text;
04: BOOL enable;
05: }
06: -(BOOL) enable;
07: -(void) setEnable:(BOOL)en;
08: @property (readwrite,assign) BOOL enable;
09:
10: @end

헤더 파일에서는 @property 구문을 선언해 준다. 그리고 구현부는 다음과 같다.

01: @implementation AccessorTest
02:
03: @synthesize enable;
04:
05: -(BOOL) enable {
06: return enable;
07: }
08: -(void) setEnable:(BOOL)en {
09: enable = en;
10: }
11: @end

접근자 구현을 전부 지우고 @synthesize 로 대체했다. synthesize 구문은 해당 프로퍼티에 대해서 접근자를 생성하도록 컴파일러에게 지시하는 역할을 한다.

* synthesize 팁

외부에서 접근하는 프로퍼티 이름과 내부의 변수 이름을 항상 일치시켜야 할까 ? 그렇지는 않다. 많은 개발자가 다음과 같은 기법을 사용한다.

01: // 헤더
02: @interface AccessorTest2 : NSObject {
03: BOOL _enable;
04: }
05: @property (readwrite,assign) BOOL enable;
06: @end
07:
08: // 구현
09: @implementation AccessorTest2
10:
11: @synthesize enable = _enable;
12:
13: @end

즉 실제 내부 변수를 언더바(_) 문자로 시작하게 선언한다. 그리고 외부에서 접근할 때는 언더바를 제거한 문자로 접근하도록 하는 것이다. 이렇게 해서 내부에서 접근할 때와 외부에서 접근할 때의 차이를 쉽게 구분하도록 한다.

self.enable = YES; // 접근자를 통한 설정
_enable = YES; // 객체 내부에서 접근

위 코드와 같이 접근자로 접근하는 것과 내부에서 접근하는 경우를 확연히 구분할 수 있다. 이 때 synthesize 에 = 가 사용된 것은 enable 이라는 프로퍼티가 _enable 이라는 멤버 변수를 사용한다는 것을 알리기 위한것이다.

* 프로퍼티 선언

다음으로 @property 에 속성을 설정하는 방법을 알아보자. 코드의 5행을 보면 @property 구문에 readwrite, assign 이라는 속성을 별도로 적용한 것을 볼 수 있다. 이러한 속성을 기술하는 이유는 컴파일러가 접근자를 자동으로 생성할 때 세부 동작 방식을 결정하기 위해서 이다. 예를 들어가며 하나씩 알아보자.

01: @property (getter = isEnable, setter = setEnable) BOOL enable;

이 코드는 접근자의 메서드 명을 직접 설정할 때 사용한다. 컴파일러는 @synthesize 로 선언한 프로퍼티의 접근자들(getter/setter) 중에서 직접 구현되지 않은 것들을 직접 생성하는데, 이 때 getter 와 setter 이름을 위와 같이 지정해 놓으면 해당 이름을 갖는 접근자를 생성한다. 하지만 특별한 경우를 제외하고는 바꾸지 않도록 하자. 대부분의 경우 접근하려는 변수가 BOOL 형식일때 isProperty 라는 이름으로 getter 를 변경할때 만 사용된다.

01: @property (readonly, assign) BOOL enable;

이 코드는 enable 프로퍼티를 오직 읽기만 가능하도록 설정한다. 즉 setter 메서드를 생성하지 않는다. 그래서 만약 self.enable = YES; 와 같이 설정하려고 하면 컴파일 에러를 발생시킨다. readonly 를 따로 설정하지 않으면 readwrite 를 설정한 것과 같다. readwrite 속성은 따로 기술하지 않아도 기본값으로 적용이 된다.

01: @property (nonatomic, retain) UIImage* img;
02: @property (nonatomic, copy) NSString* name;
03: @property (nonatomic, assign) BOOL enable;

위 3가지 사용법을 보자. 먼저 nonatomic은 다중 쓰레드에 대해서 고려할지를 선택하기 위한 것이다. 만약 다중 쓰레드 환경에서 여러 쓰레드가 경쟁적으로 접근하는 프로퍼티가 있다면 atomic 으로 설정해야 한다. 하지만 그외의 대부분의 경우 nonatomic 을 사용한다.

다음으로 가장 중요하고 또 가장 햇갈리기도 한 retain, assign, copy 를 보자. 이 3가지 속성은 각각 설정자(setter) 의 동작을 결정한다. 각각을 정리해 보자.

* retain

01: @property (nonatomic, retain) UIImage* img;

위 코드는 retain으로 설정되어 있다. 그래서 컴파일러는 다음과 같은 코드를 만들어 낸다.

01: -(void) setImg:(UIImage*) newImg {
02: if (img != newImg ) {
03: [img release];
04: img = [newImg retain];
05: }
06: }

4행이 핵심이다. 즉 img 에 새로운 객체를 설정하면, 그 새로운 객체에 retain 을 한번 호출해 준다. 이는 외부에서 그 객체를 release 하더라도 객체가 메모리 해제되지 않도록 유지하기 위한 것이다. 당연히 retain 속성은 Objective-C 객체만을 대상으로 해야한다.

* copy

@property (nonatomic, copy) NSString* name;

위 코드는 copy 속성을 이용한다. 컴파일러가 만들어 내는 코드는 다음과 같다.

01: -(void) setName:(NSString*) newName {
02: if (name != newName ) {
03: [name release];
04: name = [newName copy];
05: }
06: }

copy 의 경우는 4행에서 처럼 설정하려는 객체의 값을 복사해서 자신이 가지고 있도록 한다. copy 메서드는 기본적으로 retain 을 한번 호출하기 때문에 따로 retain 을 추가로 호출하지는 않는다. copy 는 값을 복사해서 새로운 객체를 만들기 때문에 인자로 전달되는 객체가 자주 변경될 소지가 있는 경우에 적합하다.

copy 의 경우는 Objective-C 객체 중에서도 NSCopying 프로토콜을 따르는 것들만 적용이 가능하다는 점을 주의하자.

* assign

@property (nonatomic, assign) BOOL enable;

assign 은 가장 단순한 방법으로 그냥 값을 대입시킨다.

-(void) setEnable:(BOOL) en {
enable = en;
}

Objective-C 객체를 assign 으로 설정하는 경우는 외부에서 참조 카운트를 감소시켜 객체가 해제될 위험이 있기 때문에 주의해서 사용해야 한다. 그렇기 때문에 assign은 객체가 아닌 BOOL, int 등의 일반 값에 대해서 적합하다.

* 메모리 해제

앞에서 설명한 copy, retain 으로 지정한 프로퍼티들은 그 객체가 해제될때 같이 해제하는 작업이 필요하다. 당연히 copy 와 retain 은 객체에 대해서 retain 을 호출해서 참조 카운트를 증가시키기 때문에, 객체가 해제될 때 같이 release 메서드로 해제해야 한다.

01: -(void) dealloc {
02: [name release];
03: [img release];
04: [super dealloc];
05: }

Creative Commons License
Trackback 0 : Comment 0

Trackback Address :: http://maclove.pe.kr/trackback/28 관련글 쓰기

Write a comment


Notification (통보) 사용하기

개발 & SDK 2009/06/08 01:08

통보는 하나 이상의 객체가 어떤 이벤트를 받고 싶을때 사용한다. 델리게이트가 1:1 관계로 객체간에 긴밀하게 연결되어 있다면, 통보 서비스는 동적으로 임의적인 객체간의 연결이 가능하다.

어떤 객체가 관심 있는 이벤트에 대해서 통보를 받기 위해서는 통보 센터에 자신을 등록해야 한다. 다음 코드를 보자.

01: [[NSNotificationCenter defaultCenter]
02: addObserver:self
03: selector:@selector(launchFinished:)
04: name:UIApplicationDidFinishLaunchingNotification
05: object:nil];

위 코드는 임의의 객체가 UIApplicationDidFinishLaunchingNotification 라는 통보를 발생시키면 self 객체의 launchFinished 를 호출하도록 한다. 코드의 2행과 3행의 인자는 통보가 발생했을 때 호출할 메서드를 지정한다. 여기서 이벤트는 문자열로 구분된다. 그래서 UIApplicationDidFinishLaunchingNotification 도 문자열로 정의되어 있다.

5행의 object 인자는 감시할 개체를 지정하기 위한 것이다. 만약 nil 을 지정하면 모든 객체를 대상으로 이벤트를 감시하게 된다. 만약 5행의 object 를 nil 로 설정하지 않고 특정 객체를 지정해 주면, 그 객체가 일치하는 문자열의 통보를 발생시킬 때만 통보를 받을 수 있다.

통보 이벤트를 더이상 감시할 필요가 없을때는 다음과 같이 제거해야 한다.

01: [[NSNotificationCenter defaultCenter] removeObserver:self];

removeObserver 메서드로 자신(self)이 더이상 통보를 받지 않겠다고 설정한다.

<그림1> 도움말 문서의 통보 항목

클래스의 도움말 문서를 보면 그 클래스가 발생시키는 통보를 확인 할 수 있다. 예를들어 그림1과 같이 UIWindow 클래스의 도움말을 보자. Notifications 라는 항목으로 자신이 발생시키는 통보에 대해서 설명하고 있다. UIWindowDidBecomeVisibleNotification 와 같은 통보 이름은 클래스의 헤더파일에 문자열로 정의되어 있다.

만약 직접 작성한 클래스에서 통보를 발생시키고 싶다면 어떻게 할까 ? 무척간단하다.

01: [[NSNotificationCenter defaultCenter]
02: postNotificationName:@"myEventOccured" object:self];

NSNotificationCenter 의 postNotificationName 메서드를 이용하면 된다. object 인자는 누가 이 통보 이벤트를 발생시키는 지를 설정한다.

<그림2> 예제

그림2의 예제를 직접 구현해 보자. ObjectA,ObjectB,ObjectC 는 각각 event1, myEventOccured,모든 통보 이벤트(nil) 에 관심이 있다.

01: [[NSNotificationCenter defaultCenter]
02: addObserver:ObjectA
03: selector:@selector(myEventHandler:)
04: name:@"event1"
05: object: nil];
06:

01: [[NSNotificationCenter defaultCenter]
02: addObserver:ObjectB
03: selector:@selector(myEventHandler:)
04: name:@"myEventOccured"
05: object: nil];
06:

01: [[NSNotificationCenter defaultCenter]
02: addObserver:ObjectC
03: selector:@selector(myEventHandler:)
04: name: nil
05: object: nil];
06:

ObjectC 는 name에 nil 을 설정했기 때문에 모든 이벤트를 통보받게 된다. 이제 객체X가 통보를 발생시킨다.

01: [[NSNotificationCenter defaultCenter]

02: postNotificationName:@"myEventOccured"

03: object:ObjectX

04: userInfo:[NSDictionary dictionaryWithObject:@”value1” forKey:@”data1”]

05: ];

위 코드가 실행되면 통보 센터는 ObjectX 에서 myEventOccured 통보가 발생했기 때문에, 이 통보를 감시하는 모든 객체의 메서드를 호출해 준다. 이때 감시 객체들은 ObjectX가 전달한 userInfo 딕셔너리 객체도 사용할 수 있다. 이벤트를 통보받는 메시지를 구현해 보자.

01: - (void) myEventHandler:(NSNotification *) notif
02: {
03: NSLog(@“%@ from %@”, [notif name], [notif object]);
04: NSDictionary *userInfo = [notif userInfo];
05: // userinfo 딕셔너리의 내용을 나열하자.
06: for (NSString *key in userInfo) {
07: NSLog(@“%@ = %@”, key, [userInfo objectForKey:key];
08: }
09: }

통보를 받으면 호출되는 이 메서드는 인자로 NSNotification 객체를 받는다. 이 객체는 통보에 대한 정보를 담고 있다. userInfo 속성은 통보를 보내는 객체가 추가 정보를 전달할 때 이용된다.

* 통보 큐

통보를 발생시킬때 호출하는 postNotificationName 메서드는 곧바로 모든 감시자의 메서드를 호출한다. 하지만 통보 큐를 이용하면 쓰레드가 유휴(Idle) 상태 일 때 메서드를 호출하게 만들 수 있다. 다음 코드를 보자.

01: [[NSNotificationCenter defaultCenter]
02: addObserver:objectA
03: selector:@selector(eventHandler:)
04: name:@"appLaunched" object:nil];
05:
06: [[NSNotificationQueue defaultQueue]
07: enqueueNotification:[NSNotification
08: notificationWithName:@"appLaunched"
09: object:self]
10: postingStyle:NSPostWhenIdle];

10행의 NSPostWhenIdle 옵션은 현재 쓰레드가 유휴상태일때 통보를 보내도록 한다.

Creative Commons License
Trackback 0 : Comments 3

Trackback Address :: http://maclove.pe.kr/trackback/27 관련글 쓰기

  1. 곰돌이 2010/02/16 16:56 Modify/Delete Reply

    설명감사해요

  2. biru99 2010/03/24 15:04 Modify/Delete Reply

    책을 보고 아무리 연구 해도 안나오길래 질문해 봅니다.
    A 버튼을 누르면 notification 등록을 하구요
    B 버튼을 누르면 쓰레드를 생성해서 norificationque 를 이용해서 데이터를 C 이벤트에 전송합니다 쓰레드는 무한 루프.
    C 이벤트에서 데이터를 받아서 UILabel 에 출력
    이 제가 원하는 일인데요

    현상황
    C 이벤트에서 notificationque를 전송 받긴 했는데
    UILabel 에 출력 되지가 않습니다.

    어떻게 해야 좋을지

    설명이 부족 하시면
    메일 주시면 메일로 간단한 제 소스를 전송 해 드리겠습니다.

  3. 샘플 2010/03/24 20:56 Modify/Delete Reply

    책에 있는 샘플로 provider를 컴파일 하고 OpenSSL 을 설치하였으며, 디렉토리까지 연결해 주었는데도 컴파일 에러가 나네요. 참고로 0.9.8m 임.

    error C2011: 'sockaddr' : 'struct' type redefinition

    아마도 컴파일 셋팅이 잘못된것 같기도 하지만, 아닌것 같기도 하고.. 잘안됨.

Write a comment


iPhone 어플리케이션의 실행 구조

개발 & SDK 2009/04/26 13:32

어플리케이션이 처음 실행되고 부터 종료될 때 까지의 흐름을 살펴보도록 하겠다. 맥이나 윈도우 모두 그래픽 기반의 운영체제이다. 그래픽 기반의 운영체제에서 어플리케이션을 구현시키는 가장 좋은 방식은 이벤트 드리븐(Event driven)이다. 이벤트는 사용자가 마우스나 키보드를 누르는 것부터 특정 시간이 지났다는 이벤트, 네트워크로 데이터를 받았다는 이벤트 등등 모든 것이 해당할 수 있다. 놀랍게도 세계에서 가장 발전한 상용 GUI OS들인 맥 OS , MS의 윈도우즈 들의 아름다운 UI 밑에는 가장 기본적인 뼈대 Main Loop이 버티고 있다. 사실 컴퓨터 소프트웨어가 하는 일은 최초의 컴퓨터가 발명된 때와 별반 다르지 않다. 그 위에 수만 겹의 추상화가 지금의 컴퓨팅 환경을 만들어 놓았지만, 그 뼈대는 변하지 않은 것이다. 컴퓨터는 여전히 0 과 1밖에 모르는 바보다. 크게 어렵지 않으니 하나하나 익혀나가면서 정복해 보자.

* 처음 시작

사용자가 스프링 보드에 있는 어플리케이션의 아이콘을 누르면 어플리케이션이 실행된다. 다음 코드를 보자.



Xcode 에서 자동으로 생성해 주는 코드이다. 기존의 C 함수와 마찬가지로 아이폰OS 에서 실행되는 프로그램들도 시작점은 메인 함수 이다. 메인 함수는 가장 먼저 자동해제 풀을 하나 할당한다. 자동해제 풀이란 메모리를 자동 릴리즈 하겠다고 설정한 객체들을 임시로 보관해 두었다가 적절한 시점에 해제시키기 위한 저장공간 정도로 이해하고 넘어가자.

다음 코드는 UIApplicationMain 함수를 실행시킨다. 이 함수는 argc, argv 이외에 두 개의 중요한 인자를 받는다. 첫번째로 UIApplication 클래스의 이름을 전달받는다. nil 을 입력하는 경우는 기본값인 UIApplication이 사용된다. 만약 UIApplication 을 상속받아서 새로운 클래스를 만들었다면 그 이름을 넣어주도록 하자.  두번째는 어플리케이션 델리게이트의 이름을 입력하며, nil 일 경우 info.plist 에 정의된 NSMainNibFile이 가리키는 nib파일에 설정되어 있는 델리게이트를 이용한다. 




<그림> 어플리케이션 흐름

그림을 통해서 흐름을 이해하도록 하자. 최초 main 이 호출되고 UIApplictionMain 이라는 함수 안에서 UIApplication 클래스가 하나 만들어 진다. 이 클래스는 UIKit8 에서 미리 구현해 놓은 것으로 어플리케이션 자체를 의미하는 클래스이며 한 어플리케션에 오직 하나의 UIApplication 클래스를 가지고 있다. pInfo.list 에서 UIApplication이 사용할 메인 nib 파일을 지정했다면(NSMainNibFile) 이 nib 파일을 읽어들인다. UIKit 프레임 워크는 이 파일을 분석해서 내부에 포함되어있는 객체들을 생성하고 초기화 시킨다. 보통 UIWindow 객체와 UIView 객체 등이 생성될 것이다. 그리고 인터페이스 빌더에서 설정했던 델리게이트나 객체 간의 연결도 설정한다. 이 작업이 완료되면 연결된 델리게이트의 applicationDidFinishLaunching 이 불려진다. 만약 nib 파일이 없는 경우 즉 NSMainNibFile을 설정하지 않은 경우에는 델리게이트의 applicationDidFinishLaunching 가 호출될 때 화면에 보여줄 객체들을 수동으로 만들어 줘야 한다.

그 이후 부터 UIApplication 에서는 이벤트를 처리하기 위한 메인 루프가 실행된다. 사용자가 홈 버튼을 누르거나, 전화를 받는 등의 이벤트에 의해서 어플리케이션이 종료된다. 이 때는 applicationWillTerminate 메시지가 불려지고, 이곳에서 어플리케이션의 상태를 저장하는 등의 작업을 해야 한다.

UIApplicationDelegate 는 지금 설명한 것 외에도 중요한 이벤트가 발생한 경우 그에 맞는 처리를 할 수 있는 다음 메서드들도 있다.

applicationDidReceiveMemoryWarning
메모리 부족 경고를 받은 경우. 현재 사용중인 메모리를 최대한 해제해야 한다.
applicationWillResignActive
어플리케이션이 실행중일 때 전화가 오면, 전화를 받을 것인지 무시할 지를 선택할 수 있다. 그 전에 이 메서드가 호출될 것이다. 또한 화면 잠금 버튼을 누른 경우에도 호출된다.
applicationDidBecomeActive
사용자가 전화나, 문자 등을 무시하는 버튼을 선택할 경우 다시 어플리케이션이 활성화 된 것을 알 수 있다. 처음 실행될 때 applicationDidFinishLaunching 메시지가 보내진 이 후에도 호출되고, 잠금 상태에서 다시 해제된 경우도 호출된다.
application:didFinishLaunchingWithOptions
아이폰 OS 3.0에 추가된 것으로 URL 이나 원격 통보(Remote notification) 에 의해서 어플리케이션이 실행된 경우를 처리할 수 있다.
application:didReceiveRemoteNotification
역시 3.0에 추가된 것으로 프로그램이 실행중에 원격 통보(Remote notification)을 받은 경우 필요한 처리를 할 수있다.

저작자 표시 비영리 변경 금지
Creative Commons License
Trackback 0 : Comment 0

Trackback Address :: http://maclove.pe.kr/trackback/25 관련글 쓰기

Write a comment