본문 바로가기
프로그래밍/iOS

알아봅시다 ARC(Auto Reference Counting)

by 백룡화검 2011. 11. 29.

안 녕하세요.  오랜만에 다시 뵙습니다. 그동안 프로젝트진행하고 나서 몇일 짬이 나는군요. 그래서 오늘은 핑계삼아 글을 작성해봅니다. ^^. 그런데 xcode 4.2부터 지원이 되는 ARC에 대해서 알아보시는 분들이 많더군요. 그래서 준비했습니다. 간단한 ARC 사용하기!!!


올 해 WWDC2011에서(전 가보지도 못했습니다. 개발자 비디오만 봤음. ㅠ,ㅠ; 저도 가고 싶어요!!!) ARC를 처음으로 소개하였는데요. 아직 도입 초창기 이다보니 약간의 혼란이 가중되지 않았나 싶어서, ARC에 대한 글을 올리게 되었네요. 사실 패턴관련 글들을 좀더 다듬어서 올리고 싶었는데;;; ARC가 좀더 여러분들에게 광범위하게 사용될거 같아 먼저 글을 올립니다. ^^.

먼저 이글은 Xcode 도큐먼트의 Transitioning to ARC Release Note를 참고하였음을 밝힘니다. 


그 럼 처음 보시는 분들은 머지 ARC가 머지? 삶아 먹는것인가? 간식이야? 하고 생각하실지 모르겠는데... 이건 이번에 xcode4.2에 새로 추가된 LLVM 3.0 컴파일러에 들어간 새로운 기능이랍니다. ^^. 메모리를 관리해주는 녀석이죵. 


그 럼 일단 ARC와 기존의 NSAutoReleasePool과는 어떤점이 다를가용? 그건 아마 실행 시간에 있어서의 차이입니다. 기존의 NSAutoReleasePool 같은 경우 런타임에서 객체를 풀에서 삭제하거나 추가하는 그런 역활을 가지고 있었습니다. 우리가 그동안 줄창 써왔던 retain, release, autorelease, copy등과 같은 명령어들은 사실 프로그램이 실행되어지는 런타임에서 명령어가 실행되어 객체풀을 관리해왔죠. 하지만 이 ARC라는 녀석은 이러한 작업을 컴파일 시에  이러한 작업을 미리 해두어주는 녀석입니다.  그럼 어떠한 장점이 있을까요? 기존의 런타임의 경우 retain과 release등과 같은 명령어 들은 런타임에 실행되어 지기 때문에 CPU자원을 쳐묵쳐묵했습니다. 물론 이건 미비하긴 했지만, 어쨌든 CPU자원은 쳐묵쳐묵 하고 있었던 겁니다. 하지만 ARC는 이러한 일들을 컴파일러 단에서 미리 해주기 때문에 CPU에서 retain release로 쳐묵쳐묵하던 시간이 단축이 됩니다. 애플에서 말하기를 5~6배(정확히 기억나지 않음.;;;; 어쨋든 대강) 정도 속도 향상을 높여 줄수 있다는 설명을 하였습니다. 또한 이 ARC라는 녀석은 기존의 retain release 하던 구문들이 필요 하지 않게 되었습니다. 타이핑이 줄어든것이죠. 또한 우리들이 기존에 했던 retain release 쌍으로 맞추기 및 리테인 카운트를 계산하는 것들을 이제 할필요가 없다는 점도 들수 있겠군요. 이러한 점들로 미뤄서 앞으로 많은 분들이 이 ARC라는 녀석을 이용하실 계획이실거구. 지금 보신 분들도 아 이거 써봐야 겠다 마음먹고 계실거 같습니다. 


그 런데 이게 그렇게 간단한 녀석이 아니라는 겁니다. ㅠ,ㅠ 안습이긴 하죵. 기존에 프로젝트에 적용하려면 좀 머리가 사실 아픈게 사실입니다. 저도 사실 이문제로 ARC를 써야 할까 고민중에 있죵. 일단 기존 프로젝트를 ARC를 이용할수 있게 만들려면 Xcode->Edit메뉴->Refactor->Convert To Objective C ARC라는 녀석을 눌러주게 되면 Xcode가 자동으로 리펙토링 작업을 해주게 됩니다. 하지만 만사가 그리 편한게 아니죵. Xcode는 거들뿐 모든걸 바꿔주지 않습니다. 저희가 원하는데로 직접 타이핑해서 직접 넣어줘야 하죵. 


자 기존의 코드를 리펙토링 하는 방법도 알아보았고. 이제 ARC에 대해서 좀더 자세히 들어다 보기로 하죠. ARC가 단순히 dealloc과 retain, release 구문들을 안쓰는 것만으로도 사용할수 있으면 참 좋으련만..... ARC를 사용하려면 기본 룰을 알고 계셔야 할겁니다. 

그럼 기본 룰 부터 알아보죵~!!!!


첫번째 !!!

명시적으로 dealloc, retain, release, retainCount, autorelease를 사용할수 없어요~!

@selector(retain), @selector(release) 이런식의 사용도 금지합니당~!

dealloc 함수를 사용할수는 있습니다. 물론 이 안에서 release의 사용과  [super dealloc]은 사용하면 안되요~! super까지 따라가는것은 컴파일러에서 알.아.서. 해줍니다~


두번째 !!!

NSAllocateObject 와 NSDeallocateObject 도 사용 금지!!!


세번째 !!!

NSAutoReleasePool을 사용할수 없습니다. 대신 @autoreleasepool {} 블록을 사용하셔야 되요.


네번째 !!!

memory Zone을 사용할수 없어요 NSZone 사용할 필요가 없심!!!!


다섯번째 !!! 

new로 시작되는 프로퍼티 따위는 이제 개나 줘버리세용!!!!! new로 시작되는 프로퍼티 선언은 이제 하지 마시길 바랍니다. ^^


ㅎㅎ 간단하게 ARC라는 게임의 룰을 살펴보았습니다.  자 그럼 도큐먼트에서 보여주는 약간의 코드를 살펴 보도록 하죠. (제가 짜기 귀찮아서;; 그냥 도큐먼트에 있는 소스 가져다가 설명할게요^^;)



@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 이라는 녀석이 하나 보였을 겁니다. ARC가 추가되면서 새로 추가된 명령어 입니다. 이제 사용하는 부분을 한번 살펴 보져.



- (void)contrived {


    Person *aPerson = [[Person alloc] init];


    [aPerson setFirstName:@"William"];


    [aPerson setLastName:@"Dudney"];


    [aPerson:setYearOfBirth:[[NSNumber alloc] initWithInteger:2011]];


    NSLog(@"aPerson: %@", aPerson);


}

위와 같이 alloc init구문은 여전합니다. 하지만 release구문이 사라진걸 보실수 있을겁니다. 기존에 이런 코드들은 메모리 누수의 원흉이 되었었죠. 하지만 ARC를 이용한다면 그런 걱정은 이제 끝~!


그럼 잠시 프로퍼티에 사용되어 지는 새로운 속성 두가지를 살펴 보도록 하겠습니다.

@property (strong) MyClass* myObject;

@property(weak) MyClass* myObject;


일단 두가지 부터 시작 하도록 하죠. 


strong의 경우 기존의 retain과 같다라고 생각하시면 되겠습니다. 단순히 명령어만 바뀐 것이죠.

weak 의 경우는 기존의 코드의 assign과 비슷합니다. 하지만 작동방식에 약간의 차이점을 보입니다. 객체가 해제되는 시점에서 기존의 assign같은 경우 객체가 해제 되어도 포인터 값이 변하지 않는 현상 즉, dangling Pointer라고 불리는 현상을 가지게 됩니다. 그래서 사용자가 잘못 사용할수도 있습니다. 이미 해제되어진 객체에 접근한다라던지 하는 작업 말입니다. 그런데 이 weak 이라는 녀석은 해제되는 시점에서 이 객체 포인터를 nil 값을 주게 되어 있습니다.  약간의 작동방식의 차이이지만 굉장히 편리한 녀석인거 같습니다. 


그리고 각 메소드 내에서 사용되는 변수들의 생존주기를 위한 약간의 조정자가 생겨났습니다.  총 4가지가 있는데요. 이것에 대해서 도 알아보도록 하죠.

__strong

__weak

__unsafe_unretained

__autoreleasing


__strong 은 기본 값입니다. 위에서 프로퍼티 설명과 동일합니다. __weak 역시 위 프로퍼티 선언과 동일한 역활을 하는 녀석이구요. __unsafe_unretained는 기존의 assign과 하는 일이 동일하다고 생각하시면 되겠습니다. __autoreleasing은 당연히 무슨 말인지 아시겠죠? ^^


근데 이 __weak이라는 녀석은 스택형태의 사용일때는 주의 하셔야 될거같네요.



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


NSLog(@"string: %@", string);

위와 같은 경우 객체를 할당했지만 __strong으로 할당을 안했기 때문에 할당되는 즉시 객체가 해제되어 버린다고 합니다. 그래서 로그를 찍어보면 ㅠ,ㅠ 이런 상황이 벌어지게 되는것이죠. 


자 그럼 다음 주의할점으로 ㄱ ㄱ ~


NSError *error = nil;


BOOL OK = [myObject performOperationWithError:&error];


if (!OK) {


    // Report the error.


    // ...

일단 위와 같은 코드가 있습니다. 이 녀석이 좀 애매한 코드인데 이걸 컴파일러 녀석이 어떻게 인식을 하지는 살펴보도록 하죵 



NSError __strong *error = nil;


NSError __autoreleasing *tmp = error;


BOOL OK = [myObject performOperationWithError:&tmp];


error = tmp;


if (!OK) {


    // Report the error.


    // ...


이 런식으로 컴파일러가 인식을 한다고 합니다. NSError 구문 같은 경우 기본 변수는 default로 __strong 으로 잡히기 때문에 그렇구요. 그 밑에 줄과 그 밑에줄이 문제인데요. 함수 선언에서 인자의 일반적인 선언 방식은 이렇다는 군요. 


-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;


즉 __strong을 __autoreleasing으로 변환하기 위해서 위의 두번째줄이 조정자를 변환하여 NSError 객체를 복사하는것을 볼수 있지요. 이것이 컴파일러에서 하는 일이랍니다 ^^; 참으로 오묘하고 신묘합니다. 


마 지막으로  이 ARC라는 녀석은 Objective C 구문에서만 적용되는 것이랍니다. 기존의 C, C++ 코드의 메모리관리등은 직접 해주셔야 됩니다. 물론 CoreFoundation 등의 CF로 시작되는 이름의 객체를 사용하시는 분들 역시 마찬가지겠지요.  기존의 void* 타임을 id형태로 바꿀수도 없습니다. 이걸 우회 하는 방법 역시 애플에서 마련은 해놓았습니다.  여러분의 궁금증이 다시 생길때쯤 다시 글을 올릴거 같습니다 ^^;;; 이거 쓰는데도 시간이 너무 걸려요 ㅠ,ㅠ;


자 이렇게 간단히 ARC라는 녀석을 잠시 살펴 보았습니다. 사실 편하면 편한거고 다시 작업할려니 애매한 녀석이긴 합니다만 좋은 기능은 틀림 없는 듯합니다. 미약한 글이나마 여러분들에게 도움이 되셨으면 좋겠구요. 다음 글에서 다시 찾아뵙도록 하겠습니다. 그럼 이만 ^^


오타, 첨언, 질타, 조언, 충고 감사히 받겠습니다.  글을 다시보니 너무 대충쓴거 티나네요 ㅠ,ㅠ;



출처 : http://cafe.naver.com/mcbugi/170203