최초 작성일 : 2012/01/31 10:33


오늘도 즐거운 삽질(?)의 연속~


주로 걸리는 부분이 스토리보드와 ARC  관련된 부분이다.
오늘은 ARC와 관련해서 변경된 사항 일부를 언급하고자 한다.

ARC가 컴파일러단에서 reference count를 관리해주는 기능인만큼
이와 관련된 사항들이 많이 바뀌었는데 바로 retain, release, autorelease 등이다.
오늘 언급할 부분은 이 중에 property 선언시 사용되는 키워드에 관한 내용이다.

기존에 property를 선언할 때는 아래와 같이 사용하였다.

@property (nonatomic, retain) NSObject *obj;
@property (nonatomic, copy) NSString *str;
@property (nonatomic, assign) id<MyClassDelegate> delegate;

그러나 이제 ARC를 사용하게 되면 인스턴스들의 참조 카운트를 ARC가 관리를 하기 때문에
개발자들은 가급적 retain, release 등 참조 카운트를 변경시키는 키워드나 메서드를
사용하지 않도록 권하고있다.

그럼 이제는 property 선언 시 어떻게 해야 하는가?
우선 API 문서를 좀 보자

Instead of you having to remember when to use retainrelease, and autorelease, ARC evaluates the lifetime requirements of your objects and automatically inserts the appropriate method calls for you at compile time.

즉, 개발자가 retain, release, autorelease를 언제 사용해야 하는지 기억할 필요 없이
ARC가 개발자가 생성한 객체에서 요구되는 라이프 타임을 평가하여 컴파일시에
자동으로 적절한 메서드 호출을 삽입시켜 준다는 것이다. (이전 글에도 적은 것 같은데...-.-?)

그러니까 retain은 다 빼버리자.

다시 API문서로 돌아가 올바른 클래스 선언의 예제를 보자

@interface Person : NSObject
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSNumber *yearOfBirth;
@property (nonatomic, strong) Person *spouse;
@end
 
@implementation Person
@synthesize firstName, lastName, yearOfBirth, spouse;
@end

새로운 키워드가 보인다. strong이라는 놈이 바로 그 놈이다.
strong외에 weak도 있다.

다시 API문서로 가보면

// The following declaration is a synonym for: @property(retain) MyClass *myObject;
@property(strong) MyClass *myObject;
 
// The following declaration is similar to "@property(assign) MyClass *myObject;"
// except that if the MyClass instance is deallocated,
// the property value is set to nil instead of remaining as a dangling pointer.
@property(weak) MyClass *myObject;
요로케 설명이 되어있다.
strong은 그냥 retain과 같은 의미라고 생각하면 된다.
그러니까 기존에 property 선언시 사용했던 retain 키워드는 모두 strong으로
교체해 주면 된다.

weak의 경우 조금 설명이 더 붙어있는데 일단 weak는 기존의 assign과 같은 개념이지만
dealloc 되었을 경우 대기 포인터로 남아있지 않고 바로 nil값으로 처리된다는 점이
다르다고 한다.

일단 깊숙한 부분에 대해서는 아직 이해가 부족한 만큼 그냥 retain -> strong,
assign -> weak로 대체해서 사용하면 될 것 같다.

이 때 다른 것들은 문제가 없는데 delegate의 property를 선언할 때 조금 문제가
발생하였다.

@property (nonatomic, assign) id<MyClassDelegate> delegate; 
요렇게 코딩한 것을

@property (nonatomic, weak) id<MyClassDelegate> delegate;
요렇게 수정을 하였는데 .m파일의 @synthesize 선언부에서 다음과 같은 에러가
발생을 하였다.

Existing ivar 'delegate' for __weak property 'delegate' must be __weak

뭐가 문제일까 열심히 헤매다가 역시 구글링으로 답을 얻었다.
문제는 weak로 사용하기 위해서는 변수 타입에도 weak 형태로 사용하겠다는
선언을 해주어야 하는 것이었다.

즉, 변수 선언부에

id<MyClassDelegate> delegate;
라고 코딩한 부부을

__weak id<MyClassDelegate> delegate;
요렇게 앞쪽에 __weak라는 서술자를 하나 더 붙여주어야 하는 것이다.

이 것은 strong의 경우도 마찬가지이지만 __strong은 기본값이라서 명시적으로
붙이지 않아도 된다고 한다. 즉 아무 것도 붙어있지 않으면 자동으로 __strong으로
사용이 되는 것이다.

일단 strong은 retain과 거의 동일하니 그냥 쓰면 되겠지만 __weak의 경우는
주의할 것이 좀 있다고 한다.

또 API문서 인용이다~

NSString __weak *string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];
NSLog(@"string: %@", string);

이 경우 로그에 찍히는 내용은 'string: null'이 된다고 한다
첫번째 문장에서 string이 초기화되어 할당되기는 하지만 할당되는 시점에
strong으로 참조하는 곳이 없기 때문에 바로 dealloc되버리고 만다는 것이다.

뭔소린지 이해는 잘 안가지만 암튼 __weak의 경우 메모리를 절약할 수는 있겠지만
잘못 썼다가는 bad access에 시달리게 될 것 같다.

암튼 @property 선언시에는 이와 같은 내용에 주의해서 사용을 해야 한다는 것이
핵심이다.

더 자세한 내용은 문서에서 'Transitioning to ARC Release Notes'를 참고하기
바란다.

마지막으로 한 가지만 더 설명하고 마치도록 하자
바로 Core Foundation 스타일의 변수를 Cocoa 스타일로 형변환시키는 내용이다.

우선 알아두어야 할 것은 ARC는 Core Foundation에서는 적용되지 않는다는 점이다.
즉, CFRetain과 CFRelease는 반드시 사용을 해주어야 한다.

본론으로 돌아와서
기존에는 아래와 같이 그냥 형 변환이 가능했다.

NSString *str = (NSString *)CFStringRef cfstr;

하지만 ARC를 사용하게 되면 컴파일러에게 오너쉽을 알려주기 위한 추가 코딩이
필요하다.

이 부분은 설명하기 어려워서 그냥 API 내용을 예시하는 것으로 갈음하겠다...-.-

id my_id;
CFStringRef my_cfref;
...
NSString   *a = (__bridge NSString*)my_cfref;     // Noop cast.
CFStringRef b = (__bridge CFStringRef)my_id;      // Noop cast.
...
NSString   *c = (__bridge_transfer NSString*)my_cfref; // -1 on the CFRef
CFStringRef d = (__bridge_retained CFStringRef)my_id;  // returned CFRef is +1


오늘은 너무 어려운 내용을 무리하게 설명을 하려 했더니 머리에 쥐가 난다.
아무래도 오늘 회사 업무는 셀프 휴가로 처리해야겠다...-.-

블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

Tag ARC, Xcode 4.2

댓글을 달아 주세요

최초 작성일 : 2012/01/20 10:21


오랜만에 심기일전해서 새로운 프로젝트를 생성하였다.

그런데 생성해놓고보니 Xcode 4.2에서는 처음으로 프로젝트를 생성하는 것이었네...

2가지에서 당황하였다.

먼저 스토리보드...
xib 파일은 안보이고 스토리보드만 보여서 조금 당황하고
스토리 보드 들어가서 탭바에 아이템 추가하는 방법을 몰라 더 당황하고...-.-

일단 새 탭바 아이템을 터치했을 때 보여질 뷰 컨트롤러는 기본으로 생성되는
뷰 컨트롤러 하나를 복사하였다. 복사해 놓으니 하단의 탭바 아이템이 안생긴다.
좌측의 컨트롤 리스트에서 드래그앤 드롭으로 가져다 놓으면 된다.




일단 새 탭바 아이템과 이 것을 눌렀을 때 보여질 뷰 컨트롤러를 만들긴 했는데
이 놈을 가장 좌측의 메인 창(탭바 컨트롤러라고 되어있는 놈)과 연결하는 방법을
못찾고 헤맸다. 미아를 하나 만든 셈이다...-.-

결국 경력 13년의 통밥으로 해결 아래 그림과 같이 드래그&드롭하면 연결되면서
관계가 만들어진다.

가장 좌측 탭 뷰 컨트롤러를 선택하면 우측의 커넥션 인스펙터에 다음과 같은
내용이 보인다.




빨간 색 상자 안에서 가장 우측 원을 클릭하고 드래그해서 새로 추가한 탭바 아이템과
뷰 컨트롤러쪽으로 연결시켜주면 관계(Relationship)이 추가된다.




일단 이렇게 해서 첫 번 째 당황스러운 문제 해결...^^

얼추 이렇게 만들어놓고 이제 IBOutlet으로 선언하고, @property @synthesize 
설정하고, dealloc 처리를 하려고 하는데...웬걸...release 함수 호출하는데서 죄다 에러표시가
떠버린다... ARC가 어쩌구 하는데...ARC가 뭐냐?




찾아보니 Automatic Reference Counting이란다. 그럼 이놈이 뭐하는거냐?
일단 애플의 API문서에는 다음과 같이 정리되어있다.

Automatic Reference Counting (ARC) is a compiler-level feature that simplifies the process of managing the lifetimes of Objective-C objects. Instead of you having to remember when to retain or release an object, ARC evaluates the lifetime requirements of your objects and automatically inserts the appropriate method calls at compile time.


즉 Objective-c에서 객체들의 라이프타임을 관리하는 컴파일러 수준에서 제공하는 기능으로
개발자가 일일이 객체의 retain / release  관리하게 하는 대신에 ARC 기능이 객체에 요구되는
라이프타임을 평가하여 컴파일시에 적절하게 메서드를 호출해 준다...는 야그다.

한마디로 retain 카운트 처리를 컴파일러에서 해주겠으니 개발자는 딴거나 신경써라 하는거다.

어쨌든 개념은 탑재했는데...문제는 이전 버전에서 생성된 외부 라이브러리를 사용할 경우나
이전 소스코드가 무지 많아 엄청나게 많은 dealloc 함수를 없애야 한다면...어쩌나...?

그래서 또 검색을 해봤더니 다 방법이 있었다.
일단 무심결에 지나갔지만 프로젝트 생성시에 ARC 사용을 해제하는 옵션이 있다 하니
기존 코딩 방식을 고수하고자 하면 이 옵션을 해제 하면 될 것이고. 나처럼 아무생각없이
지나쳐서 그런 옵션이 있었는지도 모르고 있는 사람이라면 다음과 같이 처리하면 된다.

1. 프로젝트를 선택하고 Build Phases탭으로 가자
2. Compile Sources 항목을 선택하자.
3. ARC를 사용하지 않을 소스 목록을 모두 선택하자
4. 키보드 Enter를 친다 (그러면 작은 입력 창이 나타난다)
5. -fno-objc-arc를 입력하고 Done 버튼을 눌러 종료한다.
6 끝...









이러면 빌드할 때는 정상적으로 되지만 버그인지 원래 그런지 에러 표시(빨간 원)는 없어지지
않는다.

암튼 4.2에서 첫 프로젝트 생성 후 당황했던 2가지 문제를 이렇게 처리하였다...^^

블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요