Objective-C ARC에 대한 이해[발번역] - Objective-C

Objective-C에서 ARC에 대한 이해

원문 : http://longweekendmobile.com/2011/09/07/objc-automatic-reference-counting-in-xcode-explained/

ARC는 메모리 관리를 위한 무거운 짐을 없애준다. 릴리즈를 덜 해서 메모리가 세거나 많이해서 죽이는 버그를 찾느라 고생하는 일은 말도 마라!

이렇게 멋지지만, ARC도 조금은 신경 써줘야 한다.

ARC에 대해서 설명해줄테니 읽어보고 빨리 배우시길

뭐가 바꼈나?

ARC 이전에 사람들은 retain/release/autorelease 를 오브젝트에  수동으로 때리면서 필요한동안 남겨놓으라고 지시 했었다.

하지만 retain 하는 것을 까먹거나 release를 많이 하면 앱이 죽거나 메모리가 세는 문제가 발생한다

Xcode 4.2에서부터 문법을 사용한다고 하기만 하면 애플의 새로운 LLVM Compiler가 수동으로 메모리 관리를 하느라

진흙길에서 고생하던 것을 언제 객체가 해제되어야 하는지 알아서 해준다. 애플 문서는 ARC를 이렇게 설명한다.

Automatic Reference Counting(ARC)는 컴파일러 수준에서 코코아 어플리케이션들의 객체의 일생(메모리관리)을 관리해주는 과정을 단순화 시켜주는 기능이다.

이 기능은 메모리 관리를 대부분 쉽게 해주긴 하지만, 다른 객체의 참조를 관리하는 클래스에서는 여전히 어떤 책임을 져야한다.

첨부터 시작해보자

레퍼런스 카운트되는 메모리(기존방식) : 훝어보기

수동으로 관리하는 참조의 숫자를 세는 방법은 iOS에서 다음과 같이 동작한다 : 객체를 만들 때 alloc/init ( 혹은 비슷한 명령어)를 사용하고, retainCount가 1로 리턴한다.

그 뜻은 객체의 주인이라는 것이다.

NSObject *obj = [[NSObject alloc] init];

// do some stuff

[obj release];

객체에게 alloc/init을 하는것(소유권을 갖는 일)과 release를 하는 것(소유권을 놔주는) 사이에 당신이 원하던 일을 안전하게 객체게 해제된다는 걱정 없이 할 수 있다.

비슷하게 autorelease pool에 객체를 넣을 때도 당신의 객체는 더 이상 필요가 없기 전까지 잘 있을 것이다.

-(NSObject*) someMethod {

  NSObject *obj = [[[NSObject alloc] init] autorelease];

  return obj; // will be deallocated by autorelease pool later

}

Automatic Reference Counting이 동작하는 원리

대부분의 iOS를 처음 접하는 프로그래머들은 reference counted memory 때문에 고생을 한다. ARC는 컴파일을 하기 이전에 retain/release/autorelease 문을 당신의 코드에 자동으로 삽입하는 과정이다

이것은 Garbage Collection과는 다르다. Reference counted memory가 사라지는 것은 아니고 그것이 자동화 되는 것이기 때문이다. 간단하다고 단순히 생각할 수도 있겠지만 Objective-C의 소스파일을 컴파일 하기 전에 전처리를 통해 구현되어야하는 많은 기능을 생각해보라.

ARC를 사용하면 당신의 코드는 이렇게 짜면 된다.

NSObject *obj = [[NSObject alloc] init];

// do some stuff

ARC 전처리기는 자동으로 이렇게 바꿀 것이다

NSObject *obj = [[NSObject alloc] init];

// do some stuff

[obj release]; // **Added by ARC**

아래의 그림(애플 문서에서 발췌)은 retain과 release 논리를 쓰고 . 이것은 노련한 Objective-C coder들에게는 맞지 않겠지만 Objective-C를 처음 접하는 사람들에게는 해당될 것이다. 그들이 팝업을 띄우면 메모리 문제가 생길 것이다.


http://developer.apple.com/library/mac/#releasenotes/MacOSX/WhatsNewInOSX/Articles/MacOSX10_7.html

프로젝트에 ARC켜기

ARC를 켜기 위해서는 간단히 Xcode 프로젝트 Build Setting에 있는  Objective-C Automatic Reference Counting이라는 옵션을 YES로 바꾸기만 하면 된다. 그러면 뒤에서는 -fobjc-arc 라는 컴파일러 flag를 켜는 것이다.

ARC 때문에 강제되는 새 룰

ARC를 켜서 컴파일 할 때는 몇가지 지켜야할 룰이 있다.

1. Alloc/Init

위에서 설명했듯이 오브젝트 생성은 끝난다. 하지만 retain/release/autorelease 는 쓰면 안된다. 간접적으로 selector에서 간접적으로 호출 하는 것도 할 수 없다. @selector(retain) 과 @selector(release) 사용이 금지된다.

2. Dealloc Methods

일반적으로 이것을 위해 존재하는 것이다. dealloc을 강제로 부르면 안된다. 하지만 custom한 dealloc 메소드는 만들 수 있고 필요하면 인스턴스를 해제할 수 있지만 [super dealloc]은 호출하지 말라. 이것은 컴파일러가 해줄 것이기 때문이다.

3. Properties의 선언

ARC 이전에는 컴파일러에게 public properties는 assign/retain/copy 파라메터로 메모리 관리를 해왔다. 이 파라메터들은 더 이상 ARC에서는 사용되지 않는다. 대신에 이 properties가 얼마나 많이 사용되는지 알려주기 위한 

weak과 strong 파라메터가 생겼다.

4. C 구조의 Object 포인터

이것 역시 하면 안된다. 이 문서는 struct대신에 class로 저장하기를 권장한다. ARC에게 모르는 것이라고 하는 것 보다 말이 된다. 이것 때문에 골치 아플 수도 있다. 하지만 ARC 설정을 file 마다 따로 할 수 있다. "ARC를 사용하지 않는 코드 포함하기" 편을 봐라

5. id 와 void*의 형변환

id 와 void*의 형변환은 Core Foundation의 C 라이브러리와 Foundation Kit의 Objective-C 라이브러리 사이에서 자주 쓰인다. 이것은 Toll Free Bridging으로 알려져 있다.

ARC에서는 CF 객체가 메모리 관리를 위해 제어권을 갖는지 필요없는지 컴파일러에게 힌트/수식어를 줘야된다. 이 수식어(qualifier)는 __bridge, __bridge_retain 그리고 __bridge_tranfer를 포함한다. 그리고 CFRetain, CFRelease를 여전히 호출해야 된다.

이것은 심화과정 주제이며 CF objects가 뭔지 모르면 걱정안해도 된다.

6. NSAutoReleasePool을 대체할 @autoreleasepool

ARC 컴파일 코드는 NSAutoReleasePool 객체를 사용하면 안된다. 대신 @autoreleasepool{} block을 사용해라. ARC를 사용하는 프로젝트의 main.m은 좋은 예제이다.

int main(int argc, char *argv[])

{

  @autoreleasepool {

    return UIApplicationMain(argc, argv, nil, NSStringFromClass([ExampleAppDelegate class]));

  }

}

7. 기타등등

Zone 기반 메모리 관리는 이제 그만(분명히 런타임도 물론 아니다)

NSAllocateBoject나 NSDeallocateObject도 쓸 수 없다.

ARC 수식어(Qualifiers) - Declared Properties

프로그래머들은 변수나 상수를 만들 때 전역으로 할 것인지 지역으로 정의할 것인지 결정을 내려왔다. 마찬가지로 우리의 properties가 다른 객체와 어떤 관계인지 결정을 내려야 한다. 우리는 strong/weak 수식어를 통해 컴파일러에게 관계를 알려줘야 한다.

Strong References

strong reference는 객체가 deallocated되면 함께 멈추는 객체에 쓴다. 다른말로 소유 관계다. 이전에 이렇게 사용하던 것들을

// Non-ARC Compliant Declaration

@property(retain) NSObject *obj;

ARC에서는 아래와 같이 클래스 인스턴스의 레퍼런스 객체 소유관계를 확실히 한다.(예를들어 소유자가 해제되기 전까지 해제되지 않는다)

// ARC Compliant Declaration

@property(strong) NSObject *obj;

Weak References

weak reference 는 객체가 해제 되더라도 멈추지 않는 객체에 쓴다.  다른 말로 소유 관계가 아닌 것이다. 예전에 이렇게 쓰던 것들을

// Non-ARC Compliant Declaration

@property(assign) NSObject *parentObj;

ARC에서는 아래와 같이 객체를 소유하지는 않지만 참조한다고 확실히 한다.(예를 들어 자식이 부모를 소유하지 않을 때 weak reference를 쓴다)

// ARC Compliant Declaration

@property(weak) NSObject *parentObj;

ARC 수식어 - 일반 변수

변수 수식어

위의 예제들은 properties가 어떻게 관리 되어야 하는지 선언하는 것이었다. 일반 변수에 대해서는 아래와 같은 것들이 있다.

__strong

__weak

__unsafe_unretained

__autoreleasing

일반적으로 이러한 추가적인 qualifier들은 매우 자주 필요한 것은 아니다. 마이그레이션 툴을 사용했을 때 처음 볼 것이다. 하지만 새 프로젝트에서는 필요도 없고 대부분 properties선언에서 strong/weak를 쓸 것이다.

__strong - default 이며 타이핑 할 필요도 없다. 이 말은 어떤 객체가 alloc/init으로 생성되면 현재 scope의 일생동안 retain된다. 이 current scope 는 보통 변수가 선언되는 brace를 뜻한다. for block이나 if block같이

__weak - 이것은 언제든지 파괴될 수 있다는 뜻이다. 이것은 다른 strongly reference객체가 있을 때만 사용된다. 파괴되면 __weak 변수는 nil이 된다.

__unsafe_unretained - 이것은 __weak과 같지만 object가 해제 되어도 pointer가 nil로 되지 않는다. 대신 pointer가 여전히 메달려 있다.(하지만 유효하지는 않다)

__autoreleasing - method에서 반환하기 전에 객체에 autorelease를 호출하는 것과 햇갈리지 않으면서 오브젝트를 참조(reference)로 넘길 때 사용된다. 예를 들면 NSError objects를 참조방식으로 넘길 때 [myObject performOperationWithError:&tmp]; 쓴다.

NB: 우리는 @property 선언에서 ARC가 활성화 되어 있는데 strong대신 retain을 써도 컴파일러가 warning을 내지 않는 것을 발견했다. 하지만 미래에도 명확히 하기 위해 strong으로 써라.

ARC로 기존 프로젝트 마이그레이션 하기

Xcode 4.2에는 기존 코드를 ARC로 변환하기 위한 conversion tool을 제공한다. 그리고 자동으로 마이그레이션 못하는 코드를 수동으로 변환하는데 도움을 준다.

1. non-ARC 프로젝트를 열어서 Edit>Refactor>Convert to Objective-C ARC. 를 눌러라

2. 변환하고 싶은 build targets과 파일을 선택하라.

3. 미리 검사하기를 누르고 Next를 클릭

nb : next를 누르면 LLVM 컴파일러가 프로젝트를 분석할 것이다. 만약 프로젝트에 어떤 에러가 있으면  다음 과정으로 갈 수 없다. Xcode 새 버젼에서 처음 열었으면 Clean한번 해줘라.

4. 제안된 변화를 검사하고 포함할지 제외할지 고른다음 save 눌러라

NB : 어떤 파일은 마이그레이션 안되는 것을 알아야 한다, 모든 파일(라이브러리 포함)이 마이그레이션 될 필요는 없다. ARC는 개별 파일에 각각 설정할 수 있기 때문에 compile time에 제외하는 방법은 아래에서 설명한다.

5. 마이그레이션 툴이 자동으로 컴파일러 옵션을 켜준다. build setting에서 확인해봐라(검색어 : reference counting)

ARC 컴파일 사용하지 않는 코드 포함하기

애플 문서에서는 "ARC 방식은 수동 reference counting 코드를 파일 단위로 조절한다. 어떤 파일은 수동으로 하고 싶으면 그렇게 할 수 있다"고 한다

이 말은 어떤 파일은 ARC를 사용하고 어떤 파일은 사용하지 않을 수 있다는 말이다. 컴파일 타임에 ARC에서 제외시키는 절차를 설명한다.

이글을 쓰는 시점에에도 많은 유명한 라이브러리들이 ARC 준비가 안되어 있다. 아래 절차를 따라 해보자.

1. Xcode project tree를 클릭

2. Target을 클릭

3. Build Phases tab을 선택

4. Compile Sources section을 확장

5. ARC에서 제외하고 싶은 하나 또는 더 많은 파일을 선택

6. 엔터키를 한 번친다.

7. -fno-objc-arc 를 타이핑

8. 엔티키를 다시 친다.

9. 각각 파일은 이제 -fno-objc-arc 컴파일러 옵션이 켜졌고 ARC에서 제외될 것이다.

그럼에도 불구하고 ARC를 써야하나?

Objective-C를 처음 접한다면 써야한다. reference counted memory에 대한 걱정 없이 처음부터 배운다면 그것보다 좋은 것은 없다. 기존의 라이브러리를 사용하기 시작할 때 ARC pre-compilation을 확실히 제외하는 방법에 익숙해 지지 않으면 고통이…

ARC없이 행복하게 코딩하고 있었다면 아마 "난 필요없어!"라고 생각할 것이다. 지금까지 당신한테는 맞을 수도 있겠다. 대부분의 유명한 라이브러리들이 ARC로 변환되지 않았고 Core Foundation classes들도 ARC와 잘 동작하지 않는다. CF 클래스를 사용할때는 확실히 한계가 존재하고 toll free bridging을 동작하게 하려면 migrating code인 extra qualifiers들이 붙는다.

내가 보고 읽은 바로는 ARC는 바로 사용가능하다. 하지만 익숙해지기 전까지는 새 프로젝트에서만 사용하자. ARC가 iOS 4.0 에서 하위 호완성을 가지지만 weak reference는 iOS 5.0에서만 지원된다. 아마 다른 이유로 아직 모두 마이그레이션 안된 것 같다. (더 자세히 보려면 이 글 끝에 Resources 를 보시오)

어떤 초기 보고서는 ARC가 당신의 프로젝트를 더 빨리 만들 수 있다고 주장한다. 아마도 autorelease에 더 적은 신뢰로말이다. 하지만 retain/release를 더 잘 사용해서 할 수도 있다. 하지만 요점은 이것이다. ARC는 당신의 코드에서 효율적인 접근을 자동으로 선택해준다는 것.

현재 ARC로 옮기는 것은 조금은 힘들다. 긴 주말 뒤에 새 프로젝트를 시작할 때 이것은 애플의 새 추천 접근 방법이다. 그러니까 미래의 디자인 결정을 할 때 ARC 기반으로 하고 수동 reference counting을 하지 않게 될 것이다.

TAG

Leave Comments


profileneoevoke소셜계의 김성모 

Recent Post

Recent Trackback


T-NAVI