[iOS] Automatic Reference Counting (ARC)

iOS 5부터 Apple이 LLVM Compiler 를 채용함에 따라 ARC (Automatic Reference Counting)가 iOS 개발에 적용되었다.

Java나 C#만을 다룬 상태에서 iOS 4.x 후반대에 iOS 공부를 시작한 본인으로서는 개발 입문 후 곧 소개된 ARC가 큰 힘(?)이 되었다.

ARC 관련하여 개발 서적, iOS developer reference 등을 보았지만 가장 이해하기 쉬운 자료는 WWDC 2011 자료(애석하게도 WWDC 2011 Introduction Automatic Reference Counting 설명 자료는 Apple 개발자 계정이 있어야 볼 수 있음)였다. 이번 포스팅은 WWDC 자료를 바탕으로 ARC 에 대해 정리한다.

ARC 도입 이유

< from WWDC 2011 >

애플은 ARC 도입 이유는 다음과 같다.

  • 앱의 비정상 종료 원인 중 많은 부분이 메모리 문제. 메모리 관리는 애플의 앱 승인 거부(Rejection)의 대다수 원인 중 하나. 
  • 많은 개발자들이 수동적인 (retain/release) 메모리 관리로 힘들어함.
  •  retain/release 로 코드 복잡도가 증가.

결국 개발자가 좀 더 편하게 앱 개발을 할 수 있도록 해주겠단 의미다. 개발자는 객체 관계에 더 집중하면 된단다.

ARC 란?

< from WWDC 2011 >

ARC는 Automatic Reference Counting의 약자로 기존에 수동(MRC라고 함)으로 개발자가 직접 retain/release를 통해 reference counting을 관리해야 하는 부분을 자동으로 해준다.

위 자료를 보면 Object가 노란색으로 강조되어 있다. 실제로 ARC는 Objective-C의 Object만을 대상으로 하며 이로 인해 구조체나 C 기반의 Core Foundation 사용에는 제약이 존재한다. 이에 대해서는 아래에서 다시 언급하도록 하겠다.

< from WWDC 2011 >

ARC는 GC (Garbage Collector)와는 다르게 런타임이 아닌 컴파일 단에서 처리된다.

GC는 런타임에 메모리를 검사하기 때문에 앱 퍼포먼스에 악영향을 준다. 그러나  ARC는 개발자가 직접 코딩하던 retain/release를 컴파일러가 자동으로 코드에 삽입시키므로, 동작시에는 MRC와 동일하여 성능 저하를 유발하지 않는다.

Apple은 iOS SDK 문서에서 차후 Mac OSX에서도 ARC가 GC를 대체할 것이라 밝히고 있다. 애플에서 제공하는 ARC 퍼포먼스 향상 효과는 다음과 같다.

< from WWDC 2011 >

ARC를 위한 규칙

컴파일러가 자동으로 retain/release 코드를 삽입시키기 위해 몇 가지 규칙을 제안한다. 이를 알아보자.

< from WWDC 2011 >

[첫 번째]  retain/release/autorelease 를 이용하지 마라.

더 이상  retain/release/autorelease를 코드에 직접 삽입할 필요가 없다. 컴파일러가 알아서 다 해준다. 블록(blocks)도 Object이므로 기존 블록에 사용하던 copy, autorelease는 불필요하다. 메소드의 return에 대한 autorelease도 고려할 필요 없다. 그냥 짜면 컴파일러가 알아서 해준다.

dealloc 메소드는 사용할 수는 있지만 이는 인스턴스 변수들의 메모리 해제(release)가 아닌 자원 관리 차원에서 허용된다. 또한 개발자에 의해 재정의된 dealloc 메소드에서는 [super dealloc]을 호출해서는 안된다

< from WWDC 2011 >

[두 번째 규칙] C 구조체 내에 Object 타입을 사용하지 마라.

ARC는 Object만을 고려한다. 구조체 내의 Object는 ARC가 계산하기 어렵다. 그냥 구조체가 아닌 Object  (Objective-C Class)를 사용하라.

< from WWDC 2011 >

[세 번째 규칙] Core Foundation은 Objective-C의 Object 타입이 아니므로 Core Foundation과 Object 간의 명시적인 타입 캐스팅이 필요하다.

추가로 Core Foundation 스타일의 객체에는 CFRetain, CFRelease 와 같은 메모리 관리 함수를 사용할 수 있다. 다음을 참조하자 – “Managing Toll-Free Bridging”

< from WWDC 2011 >

[네 번째 규칙] NSAutoreleasePool 사용하지 마라. NSAutoreleasePool은 내부적으로 보면 실제로는 Object가 아니다. 대신 @autoreleasepool{}을 사용하라. 속도도 더 빠르다. @autoreleasepool{}은  loop에 쓰이는 경우가 많다. NSError에도 쓰이지만 자주 사용되지는 않는다. 그래도 경험상 거의 사용할 일이 없는 듯 하다.

새로운 지시어

1) 프로퍼티 속성

아래 예처럼 strong, weak 키워드가 프로퍼티에 사용된다.

// 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과 의미가 거의 같다.

weak는 assign 키워드처럼 참조 카운트를 증가시키지 않는다.  그러나 assign과는 다르게 참조하는 객체가 해제(deallocated)되면 weak로 선언된 프로퍼티는 자동으로 nil이 대입된다. assign의 경우엔 nil 이 대입되지 않아 의도치 않은 포인터를 가리킬 수 있었다. assign과 거의 동일한 의미의 지시어는 unsafe_unretained 이다.

ARC는 강한 참조 사이클(strong reference cycle)을 방지하지 못한다. 이를 위해 개발자가 강한 참조 사이클이 발생하지 않도록 약한 참조(weak)를 잘 사용해야 한다. (참고- “Practical Memory Management”)

2) 변수 지시자(variable qualifier)

  • __strong : 디폴트 지시자. 객체의 소유권을 획득한다. (retain count 증가)
  • __weak : 객체의 소유권을 가져오지 않고 참조한다. 참조한 객체가 소멸되면 nil 로 설정된다.
  • __unsafe_unretained : __weak 와 비슷하지만 참조 객체 해제되어도 nil 로 재설정되지 않는다. 변수의 값은 특정 포인터를 계속 가리키고 있으므로 변수 사용에 유의해야 한다. 오류가 발생할 수 있다.
  • __autoreleasing : 보통 메소드의 (id *) 타입 파라미터 인수에 사용하며 함수 리턴시 자동으로 해제된다. Cocoa Framework 의 간편 생성자(Convenience Initializer)역시 __autoreleasing 타입의 객체를 반환함.

__strong, __weak, __autoreleasing 변수는 생성시 자동으로 nil 로 초기화 된다. 변수 선언 형식은 다음과 같다.

ClassName * qualifier variableName;

__weak 를 사용할 경우 아래와 같은 실수를 하지 않도록 주의해야 한다.

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

결과적으로  string 변수는 바로 nil 로 설정된다.

단계적으로 다음과 같이 이해하면 쉽다.

1) 우측 구문이 샐행되어 NSString 객체 생성.

2) NSString 객체가 string 변수에 할당

3) string 변수는 __weak으로 선언. 따라서 기존의 참조 카운트를 그대로 갖는다. 따라서 초기화된 NSString의 참조카운트 0(NSString 객체를 강한 참조로 소유하는 다른 객체 없음)이 그대로 넘어가고, 참조 카운트가 0이므로 weak 특성에 따라 바로 nil 로 재설정.

__autorelease 는 일반적으로 잘 사용되지 않는다.

기타

1) 지원 OS

  • Xcode 4.2
  • Mac OS X v10.6 and v10.7 (64 – bit application)
  • iOS 4 and iOS 5
  • weak 참조는 OS X 10.6 및 iOS 4 에서 미지원. 따라서 weak 참조를 지원하는 완벽한 ARC 기능은 OS X 10.7 및 iOS 5 버전부터

2) NSAllocateObject , NSDeallocateObject 를 사용할 수 없다. alloc을 이용해서 객체를 생성하면, Runtime이 객체 해제(deallocate)를 알아서 처리한다.

3) ARC 는 다음과 같은 메소드 네이밍에 대한 제약을 갖는다.

  • 프로퍼티 접근자(accessor) 이름이 new 로 시작해서는 안된다. new 로 이름을 명명이 가능한 경우는 아래와 같이 getter 의 이름을 다른 이름으로 명시적으로 지정했을 경우이다.
// Won't work:
@property (strong, nonatomic) NSString *newTitle;
// Works:
@property (strong, nonatomic, getter=theNewTitle) NSString *newTitle;

* Reference

Advertisements
Leave a comment

3 Comments

  1. 많은 도움이 됬습니다 ㅠㅠ!!! 감사합니다 ㅎㅎ 블로그에 링크해서 스크랩으로 퍼갑니다 😀

    Reply
  1. [iOS] 능력자의 블로그를 통해 되돌아본 ARC | Park's Park
  2. IOS 이슈 및 소스 | Fix Up Idea

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: