2012년 6월 18일 월요일

Xcode ARC, 약인가 독인가

ARC(Automatic Reference Counting)라는 기능은 XCode 4 들어서 보여준 새로운 기능이다. Objective-C에 정의된 요소이긴 하지만 사실상 Objective-C를 사용하게 되는 곳은 현재로썬 Xcode 뿐인 것 같으니 그려러니 생각하자.

ARC의 간략한 정의

ARC는 컴파일러가 메모리를 해제하는 코드를 자동으로 집어 넣고 컴파일 하는 특수한 기능이다. 예를 들면 다음 코드를 작성하면
- (void)someMethod {
    NSMutableArray *array = [[NSMutableArray alloc] init];
}
컴파일 중에 자동으로 아래 코드가 위 메서드 마지막에 붙는 셈이다.
    [array release];
물론 작성한 코드에는 아무런 변화가 생기지 않는다.

위 예는 ARC의 한 가지 예일 뿐 실제로는 좀 더 많은 기능들이 제공되지만, 본질은 위와 같이 리테인 카운트를 감소시키는 코드를 자동으로 추가해서 컴파일 하는 것일 뿐이다.

이 기능은 컴파일 단에서 이루어지므로 ARC를 사용하겠다고 한다면 기존에 쓰던 많은 메모리 관리 키워드(retain, autorelease 등등)는 사용 할 수 없게 된다. 그리고 dealloc 과 같은 메모리 관리용 오버라이드 메서드는 사용 방법이 조금 달라진다.

다행히도 기존 코드를 ARC코드로 마이그레이션 해 주는 기능을 Xcode가 제공하고는 있지만 완벽하진 않다. 어쨌든 ARC를 쓰게 되면 기존 코드 호환성은 떨어지게 된다.

ARC는 GC가 아니다

자동으로 메모리 해제 코드를 넣는 것과 자동으로 메모리를 해제(Automatic Gabage Collection)을 하는 것은 전혀 다른 이야기이다. GC(Gabage Collection)는 프로그램 실행 도중 동적으로 동작하는 기능이다.

ARC는 컴파일 단계에서 적용되는 개념이므로 GC로 인해 발생하는 퍼포먼스 저하 등이 발생하지 않는다. 어떻게 보면 GC보다 한 단계 발전한 메모리 관리 개념이기도 하다.

하지만 결국은 코드 단위의 메모리 반환 기능을 추가하는 것이기에 완벽하다고는 볼 수 없다. ARC를 맹신해서는 안된다는 이야기이다.

Strong과 Weak

Objective-C의 클래스 개념 중 가장 특징적인 기능이 property 기능이라고 생각한다. strong과 weak는 ARC에서 도입된 새로운 property 속성이다.

예를 들자면 다음과 같은 코드이다.
@property (strong) NSMutableDictionaty *dict;
@property (weak) IBOutlet UIView *someView;
...
@synthesize dict = _dict;
@synthesize someView;
기존에 쓰이던 retain 과 같은 것은 ARC에 들어서는 사용 할 수 없다고 생각하자.

strong 키워드는 retain과 비슷하다. 단지 차이가 있다면 자동으로 dealloc 내부에 해당 프로퍼티를 release 하는 코드가 컴파일 도중 삽입된다고 생각하면 된다. 즉, strong은 해당 클래스가 소유하는 멤버변수라고 생각하면 된다. 해당 클래스의 오브젝트가 메모리에서 해제되지 않는한 해당 오브젝트의 strong 멤버는 절대로 메모리 상에서 해제되지 않는다.

weak의 경우는 assign 과 비슷하면서도 다르다. weak가 붙은 프로퍼티는 해당 메모리가 해제되면 자동으로 nil로 초기화 된다. 얼핏 생각하면 대단한 기능이지만, 문제는 클래스 내부에서 메모리를 생성 한다고 했을 때 메모리가 해제되는 시점이 동일 블럭 내부이다는 점이 있다. 따라서 외부에서 특정 오브젝트 포인터를 어사인 한다던가, 인터페이스 빌더에서 연결된 아웃렛 등에서 주로 사용하게 된다. strong과 반대로 weak는 해당 클래스의 소유가 아님을 의미한다고 생각하자.

이 두 프로퍼티 속성은 기존 Objective-C 식의 코드를 사용 할 수 없게 만들 수도 있다. strong의 경우 오히려 깔끔한 편이지만 weak의 경우는 언제 어떻게 메모리가 사라질지 알 수 없다.

물론 클래스 디자인을 깔끔하게 한다면 문제 될 것은 없다.

Autorelease?

autorelease 라는 키워드는 ARC에선 오류가 발생하는 키워드이다. 그렇다면 어떻게 써야 할까? 다행히도 __autorelease 라는 키워드가 정의되어 있어서 이를 변수 선언부에 정의해 주면 비슷하게 이용 할 수 있다.

언더라인(_)이 붙는 코드는 개인적으로 가동성을 해치기도 한다고 생각한다. 그래서 왠지 좀 꺼림칙하다. 물론 개인적인 느낌일 뿐이다.

Dealloc의 변화

기존 dealloc 메서드는 [super dealloc] 을 호출해 주는 것이 필수적이다. 하지만, ARC하에선 super의 dealloc을 호출해서는 안된다. 호출하게 되면 오히려 문제가 발생 할 것이다.

dealloc에서 프로그래머가 할 일은 극히 없다고 볼 수 있다. 그래서 아예 dealloc을 만들지 않는 코드가 대부분일 것이다.

형(Type)변환과 관련된 비호환성

ARC용 코드에서는 간혹 __bridge 로 시작되는 이상한 키워드가 붙는 코드를 볼 수도 있다. 주로 struct 등 클래스가 아닌 구조체 형태의 형변환 등에서 쓰인다. 이런 순수 Objective-C 의 문법 외의 코드들은 형변환에 있어서 기존 코드를 그대로 이용 할 수 없다.

이런 브릿지 코드는 상황에 따라서는 거의 쓸 일이 없다. 그래서 개인적으론 크게 신경쓰지 않는다.

하지만 CoreGraphic 등 C API를 이용해야 할 때는 필수불가결 하게 써야 한다.

문제는 역시나 언더라인 표기법이다. 개인적으론 언더라인이 키워드 앞에 오는 경우는 중복 등을 피하거나 컴파일 상수 등을 표기할 때나 사용하던 문자라서 굉장히 꺼림칙하다. 더구나 언더라인과 소문자의 조합이라니 왠지 문화 충격적인(?) 코드이다.

무엇보다 이런 코드가 남발되면 가독성을 크게 해친다고 생각한다.

형변환 문제와 비슷하게, id라는 void 포인터와 비슷한 기능의 키워드도 무작정 사용하게 되면 문제가 발생하게 된다. id 타입은 ARC에서는 가급적 사용하지 않는 편이 좋다. 명시적으로 정확한 형을 기재하거나 형변환을 확실하게 해 주어야 한다.

결론

제목이 '약인가 독인가' 라고 했으니 정확한 결론은 '잘 쓰면 약이요 못 쓰면 독이다' 라는 어중간한 결론이 나올 수 밖에 없다.

최소한 메모리 관리 코드 일부를 안 써도 되니 (아니 쓰면 안되니) 코드가 줄어들게 되고 이는 소프트웨어 엔지니어링 개념에서 굉장히 좋은 코드가 될 수 있다. 그리고 레퍼런스 카운트 개념이 부족한 이들에게는 Objective-C를 스크립트 언어급의 사용하기 편리한 언어로 바꾸어 줄 수도 있다.

하지만, 특정 코드의 실제 내용이 상상이 안된다는 큰 단점을 가지고 있다. 프로그래머에 따라 다르겠지만 프로그래머 자신이 코딩 한 대로 동작해야 하는 것이 가장 좋은 언어일 것이다. ARC는 프로그래머가 코딩하지 않는 부분을 뒷쪽(?)에서 알아서 코딩해 버리니 자존심이 상하는 프로그래머도 있을 것이다.

간혹 메모리 이슈로 디버깅을 할 때 ARC용 코드는 난감함을 주기도 한다. strong 프로퍼티는 굉장히 편하다고 느낀다. 하지만 남발하다가 서로가 서로를 strong으로 묶어버리는 최악의 사태가 벌어지면 굉장히 난감하다. 이 경우 ARC 하에서는 절대로 메모리를 해제 할 수 없기 때문에 leak이 생기는데 이 문제 해결을 위해 디버깅 할 때 어려움을 유발하기도 한다. 물론 이 외에도 weak로 발생하는 문제 또한 디버깅의 큰 어려움으로 작용하기도 한다.

ARC의 가장 큰 단점은 기존 코드와의 호환성이 떨어지게 된다는 점이다. 많은 써드파티 모듈이 여전히 비ARC 코드라서 많은 편리함을 버려야 한다. 그리고 일부 모듈 제작자들은 ARC를 극도로 꺼리기도 한다.

아직은 ARC를 쓰기에는 이르지만 쓰고 싶다면 쓰는 것도 나쁘지는 않다. 특히 외부 모듈 의존성이 없거나 직접 만들 거라면 오히려 좋은 선택이 될 지도 모른다. 특히 스토리보드와 연계해서 ARC와 함께 개발을 하게 된다면 많은 코드가 줄어들어서 개발이 굉장히 빨라 질 수도 있다.

선택은 개인이 하는 것이다. 단지, 많은 레퍼런스와 서드파티 모듈이 필요하다면 ARC를 쓰는 건 먼 미래의 이야기일 뿐이다.

관련포스트: Objective-C의 레퍼런스 카운트는 왜 필요한 건가

댓글 없음 :