ARC (Automatic Reference Counting) 관련 키워드 사용법

The LLVM Compiler 3.0 에서 ARC기능이 생기면서 4개의 ownership 지시자가 추가되었다1.

  • __strong
  • __unsafe_unretained
  • __weak
  • __autoreleasing

동일한 키워드라도 다음과같이 변수 선언시에는 롱 언더바(__)를 붙여줘야 하고, @property선언시에는 언더바 없이 키워드만 넣어주면 된다.

@interface TreeAdditionObj : NSObject
{
    NSString* __unsafe_unretained nodeID;
}
@property (unsafe_unretained) NSString* nodeID;
@end

strong 참조

ARC를 사용할 때 모든 포인터를 strong으로 사용하면(strong 참조가 지정된 포인터 변수에 새로운 객체를 대입하면 해당 객체는 무조건 retain된다는 의미), 두 객체가 서로 참조하게되는 순환 참조(circular reference) 상황에서 메모리 해제가 불가능해진다. 일반적으로 Cocoa나 CocoaTouch개발 프레임웍상에서 객체에 delegate를 사용할 때 이러한 순환 참조 상황이 많이 발생하기 때문에, delegate들에 대해서는 주의해서 weak 또는 unsafe_unretained를 사용해야 한다.

weak, unsafe_unretained 차이점

unsafe_unretained의 경우 기존에 있던 포인터 변수에 적용된 assign 지시자와 동일하게 생각하면 된다. 이 키워드를 이용하여 선언된 포인터는 해당 포인터가 가리키고 있는 객체가 메모리에서 해제되더라도 포인팅 주소를 그대로 갖고있게된다. 때문에 해제된 메모리에 실수로 접근하게 될 경우 memory access violation 에러를 겪게될 가능성이 있다. 이러한 위험성때문에 ‘unsafe’라는 키워드가 추가되어 있다.
반면 weak의 경우 가리키고 있는 객체가 메모리에서 해제될때 포인터 변수에 자동으로 nil값을 대입해주게 되어 안전해진다.

하지만 weak같은경우는 Deployment target이 iOS 5.0, MacOS 10.7 이상에서만 동작하기 때문에, 그 이하의 버전들을 지원하기 위해서는 unsafe_unretained를 사용할 수 밖에 없다.

autoreleasing은 언제 사용?2

autoreleasing 키워드는 함수 호출시 레퍼런스 형태로 전달되는 변수를 선언 할 때 사용된다.

NSError* __autoreleasing error;
NSString* testString = @"test";
if (![testString writeToFile:path atomically:YES error:&error)
{
    NSLog("ErrorCode: %d",[error code]);
    return;
}

위 예제 코드를 보면 error 변수의 경우 함수를 호출하기전에는 단순히 포인터 선언만 되어있고 아무런 값도 지니고 있지 않다. 하지만 함수 호출 과정에서 에러가 발생했을 때만 함수 내부에서 error 변수에 객체를 할당해주게 되고, 함수가 리턴되어 돌아올때 할당되었던 객체는 autorelease 상태로 넘어오게 된다. 이처럼 참조를 함수로 전달할때 __autoreleasing 키워드를 통해 컴파일러가 참조 변수가 가리키고 있는 객체의 해제 시점을 명확히 알 수 있는것이다.


  1. http://clang.llvm.org/docs/AutomaticReferenceCounting.html

  2. https://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html