프로그래밍 언어/Objective C

ARC는 가비지컬렉션이 아니예요.

날개 2014. 1. 20. 19:03

2011년도였나요? XCode 4.2부터 ARC (Automatic Reference Counting)라는 기능이 추가되었죠.


일종의 메모리 관리를 자동화 해주는 기능인데, 커뮤니티 같은곳에 올라오는 질문을 보면, ARC 를 가비지컬렉션(GC, Gabage Collection)를 이용한 방법으로 오해하고 있는 분들이 있더군요. 심지어는 블로그나 다른 사이트들에도 ARC를 가비지컬렉션을 애플이 이름을 바꿔 놓은것 처럼 써놓은 글도 여럿 보이는군요.


ARC는 가비지컬렉션이 절대 아닙니다!


혼동하면 안됩니다. 방식 자체가 둘이 완전히 다르거든요.


Objective C는 기본적으로 Reference Counting 이라는 메모리 관리 모델을 사용합니다. Objecitve C에서만 사용되는 방법은 아니지만 (예를 들어 마이크로소프트의 COM기술에서도 사용) 어쨌든 Objective C에서는 기본적으로 메모리를 관리하는 방법이죠.


그런데, 이 역시도 어떤면에서든 수동으로 메모리를 관리하는 것이 기본이다보니(물론 autorelease도 있긴 합니다만...) 프로그래머가 사람인 이상 실수할 때도 있더라는 겁니다.


그래서인지 애플은 XCode 4.2와 iOS 4.3부터 (실질적으로는 iOS 5.0 이상에서 쓰기를 추천합니다. 그 밑으로는 지원하지 않는 기능이 있어서요...) ARC (Automatic Reference Counting) 이라는 메모리 관리 기능을 추가했습니다.


이로써 프로그래머들이 직접 메모리를 release 하지 않아도 되게 되었습니다.


오히려, ARC 기능이 추가되면서, 애플은 맥에서 지원되던 가비지 컬렉션조차 없애버렸습니다.


가비지 컬렉션 방식은, 메모리 관리를 가비지 컬렉터라는 것이 프로그램 실행중에 동적으로 감시하고 있다가, 더이상 사용할 필요가 없다고 여겨지는 것을 메모리에서 삭제해 주는 것입니다. 즉, 실행타임에서 메모리 관리를 하는 것입니다.


그와는 달리, ARC는 프로그램이 실행되고 있는 상태에서 감시하는 것이 아니라, 코드를 빌드할 때에(컴파일할 때) 컴파일러가 프로그래머 대신에 release 코드를 적절한 위치에 넣어주는 것입니다.


즉, 프로그래머가 코딩할 때에 release 코드를 넣지 않았다 뿐이지, 실제로 기계어로 번역된 바이너리에는 컴파일러가 프로그래머 대신 넣어준 release 코드가 존재 한다는 의미입니다.


이 차이점을 이해하는 것이 중요한데, ARC라는 것이 이런식으로 동작한다는 것을 알고 있어야, 효율적인 프로그램을 짤 수 있게 되기 때문입니다.


예를 들어, 어디에 메모리를 할당하는 코드를 위치할 것인지, 또는 어디서 해제가 되었으면 좋겠다라든지 이런것을 예상할 수 있어야 한다는 것이지요. 예를 들어 어느부분에서 할당한 메모리가 더이상 필요 없을때에, nil 값을 넣어주는 코드를 넣는다면, 컴파일러는 그 전에 release 코드를 넣어준다든가 하는 방식으로 말이죠.


사실 일반적인 부분은 그렇게 어렵지 않습니다. 다만 골치 아플때가 있는 것이, Objective C 코드는 ARC를 사용할 수 있는데 반해, 기존의 C/C++ 코드는 수동으로 메모리 관리를 하기 때문에 서로 같이 사용할때는 주의가 필요합니다. (특히 Objective C 코드와 Core Foundation 코드를 같이 쓸때 빈번하게 일어납니다. 예를 들어, NSString과 CFString을 혼용한다고 할때, NSString은 ARC를, CFString는 수동관리를 하게 되지요. 이럴때는 브릿지라는 것을 사용해야 합니다.)


예를 들어 아래와 같은 코드가 있다고 하죠.


- (void) testMethod 

{

MyClass *myInst = [[MyClass alloc] init];

myInst.myVar = @"Test";


NSLog (@"My Var = %@", myInst.myVar);


}


예전 ARC를 사용하지 않는 방식대로라면, 위 코드에서 메소드가 끝나기 전에 반드시 [myInst release];를 넣어줘야 myInst가 메모리에서 방황하며 차지하고 있는 일이 없게 되겠죠 (Memory Leak).


만약 가비지 컬렉션 사용하는 언어라면, 저 프로그램을 실행하고 나면 가비지컬렉터가 계속 감시하고 있다가 특정한 조건이 되면 메모리에서 제거할 것입니다.


그러나, ARC는 그런식으로 동작하지 않습니다.


ARC를 사용하면, 실제로 컴파일(빌드)하면 내부적으로는 아래와 같이 빌드되는것과 같다고 볼 수 있습니다.


- (void) testMethod 

{

MyClass *myInst = [[MyClass alloc] init];

myInst.myVar = @"Test";


NSLog (@"My Var = %@", myInst.myVar);

[myInst release];    // 컴파일러가 빌드시 자동으로 넣어줌

}


즉, 위의 빨간 부분이 빌드된 바이너리에는 자동으로 포함되어있게 되는 것입니다.


물론 위 예제는 가장 간단한 예이지만, 훨씬 복잡한 코드도 컴파일러가 적절한 곳에 해제 코드를 넣어줄 것입니다.


이건 아주 중요한 장점인데, 가비지 컬렉션이라는 것이(대표적으로 Java나 .NET에서 사용됩니다.) 항상 메모리를 차지하고 감시해야기 때문에 프로그램 자체 외에 메모리 사용량이 더 늘어날 수 밖에 없으며, 지속적인 감시를 위해 CPU를 일부 사용할 수 밖에 없는데 비해, ARC는 어차피 수동으로 개발자가 넣을 코드를 컴파일러가 넣어주는 것이기 때문에, 전혀 그런 오버헤드가 필요 없다는 것입니다.


이는 특히 메모리와 CPU가 데스크탑에 비해 한정적인 모바일기기에서는 더 중요한 문제입니다. (하드웨어가 계속 발전한다고는 하나 굳이 불필요하게 메모리와 CPU를 차지하고 배터리를 더 소모할 이유는 없습니다.)


어떻게 그런 코드를 알아서 넣어 줄 수 있을정도로 분석을 하는지까지는 신기할 따름이지만, 개인적으로는 예전부터 XCode에 들어있던 기능인 Analyze를 한번 돌려보면 대략 감이 올것 같기는 합니다. (ARC를 사용하지 않고 있다면, 본인 프로젝트로 한번 Analyze를 해보세요. 메뉴는 Product->Analyze (Shfit+Cmd+B)입니다.) ARC없이 프로그램짜다가 중간 중간 Analyze를 돌려보면 어디어디에 release가 있어야 할것 같은데 없다라든지 이런걸 다 분석해 주거든요.)


어쨌든, ARC와 가비지컬렉터는 전혀 다른 것이기 때문에 가비지컬렉터로 생각하고 프로그램짜면 안된다는 점과, ARC의 특성을 잘 이해하고 효율적인 프로그램을 구현하시길 바랍니다.





끝.


참고자료 (애플 개발자 계정 로그인이 필요할 수 있습니다.)