'아이폰SDK'에 해당되는 글 4건

  1. 2009/06/19 Clang으로 코드 오류 찾기 (1)
  2. 2009/06/16 iPhone SDK 메모리 관리 기법 (2)
  3. 2009/04/26 iPhone 어플리케이션의 실행 구조
  4. 2009/04/11 Xcode에서 프리 어플리케이션을 위한 타깃 설정

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


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


Xcode에서 프리 어플리케이션을 위한 타깃 설정

개발 & SDK 2009/04/11 01:31

필자도 지난 3월에 처음으로 아이폰 어플리케이션을 AppStore에 등록했다.



물론 기대는 컸지만, 현실은 냉혹했다. ㅠㅠ
하지만 좀더 많은 사람들이 썼으면 하는 마음에서 프리버젼을 배포해야 겠다고 생각했다. 그래서 Xcode 의 프로젝트 에서 어떻게 쉽게 Lite 버젼을 위한 타깃을 자동으로 만들고, 하나의 프로젝트를 그대로 유지할 지 생각해 봤다.
기존의 많은 개발툴과 방식은 같다. 일단 내가 사용한 방법은, Target 을 하나 추가하는 것이다.
AAA 라는 기존의 타깃에서 AAA Lite 라는 타깃을 추가하는 것이다.



위의 경우 aurioTouch 라는 타깃 하나만 있다. 여기서 다음 메뉴에서 Add Target ... 을 통해서 타깃을 추가하도록 하자.



선택하면 창이 하나 뜨는데 Application 타깃이므로 Application을 선택하고 이름을 입력하도록 하자.






타깃이 추가되었기 때문에, 이제 타깃에 해당하는 프리프로세서를 이용해서 코드 상에서 일반 버전과 프리 버전간의 차이를 만들어 나가면 된다. 프리프로세서를 추가하는 방법은,

2009/04/05 - [개발 & SDK] - Xcode 에서 프리프로세서(Preprocessor) 설정하기

에서 보도록 하자. 원리는 두개의 타깃에 대해서 프리프로세서의 값을 다르게 두는 것이다. 그리고 소스에서는 프리프로세서의 값을 따라서 다른 동작을 하도록 만들 면 된다.




나름 괜찮은 팁이라고 생각하서 몇일 전 부터 벼르고 있었는데, 막상 작성하고나니 약간 허무하다.


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

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

Write a comment