본문 바로가기
프로그래밍 놀이터/iOS

[Objective-C] 참조 카운터를 사용한 메모리 관리 방법

by 돼지왕 왕돼지 2017. 12. 27.
반응형

 [Objective-C] 참조 카운터를 사용한 메모리 관리 방법


출처 : OS X 구조를 이해하면서 배우는 Objective-C Chap 5.

0, 1, @authreleasepool, alloc, annotation, arc, arc 개요, arc 란, arc 변환, arc 자동 변환, ARC 프로그래밍의 기타 주의사항, auto release, automatic reference counting, autorelease, Break, build option, clang, compile option, convenience constructor, COPY, dangling pointer, dealloc, fobjc-arc, gcc, Goto, init, initialize, IOS, lifetime qualifier, Mac OS X, malloc, mrc arc, mutablecopy, new, nil 대입, nsautoreleasepool, nserror, nserror**, nsobjcruntime.h, NS_RETURNS_NOT_RETAINED, NS_RETURNS_RETAINED, objc_arc, object graph, Objective-C, Objective-C 2.0, Objective-C Automatic Reference Counting, out, ownership policy, pass by reference, pass by writeback, pass-by-writeback, release, retain, retain cycle, retain release 재정의, retainCount, Return, run loop, selector, super dealloc, void* casting, zeroing, [Objective-C] 참조 카운터를 사용한 메모리 관리 방법, _init, __autoreleasing, __bridge, __has_feature, __strong, __unsafe_unretained, __weak, 가비지 컬렉션, 객체 그래프, 객체 소유권, 객체 저장 세터 메서드, 객체를 포함한 c배열, 구조체 관련 제약, 댕글링 포인터, 동적 메모리 관리, 디버그용, 라이트백 전달, 라이프타임 수식자, 매크로, 메모리 관리, 메서드, 메서드 dealloc 정의, 메서드 패밀리, 백포인터, 변수 제로화, 사용법, 섞어 쓰기, 셀렉터, 소유권, 소유권 수식자, 소유권 정책, 수동 카운터 관리 방식, 수동 카운터 조작 금지, 실행 반복, 약한 참조, 어노테이션, 옵션, 유지 순환, 유지 순환과 약한 참조, 유클리드 소거법, 인스턴스 메소드, 인스턴스 자동 해제, 인스턴스 해제, 임시 객체, 임시 인스턴스 생성, 자동 해제 풀, 접근자 메서드, 정적인 객체, 주의사항, 주의점, 참조 카운터, 참조 카운터 확인, 카운터 관리 방식, 컴파일, 컴파일러, 컴파일러 지정, 편의 생성자, 허상 포인터


Notice : 정리자(돼지왕 왕돼지)가 remind 하고 싶은 내용이나 모르는 내용 기반으로 정리하는 것이기 때문에 구체적인 내용은 책을 사서 보시기를 권장드립니다.


5.1. 동적 메모리 관리


* 5.1.1. 메모리 관리의 필요성


-

이미 해제된 부분을 가리키는 위험한 포인터를 댕글링 포인터(dangling pointer) 또는 허상 포인터라고 부른다.




* 5.1.2. 카운터 관리 방식과 ARC, 가비지 컬렉션


-

Objective-C 2.0 에는 카운터 관리 방식과 다른, 가비지 컬렉션이라는 메모리 관리 방식도 이용할 수 있게 되었다.

가비지 컬렉션은 프로그램의 일반 실행과 달리 불필요해진 객체를 찾는 구문이 자동으로 동작해서 해제하므로 카운터 조작처럼 코드를 프로그램에 넣지 않아도 된다.



-

프로그램을 개발할 때는 수동 카운터 관리 방식, ARC, 가비지 컬렉션 중 하나를 선택해야 한다.

Mac OS X 10.7 Lion 및 iOS 5 이후라면 ARC 를 사용해 개발하는 방법이 추천된다.




5.2. 수동 카운터 관리 방식


* 5.2.1. 참조 카운터


-

retain 을 실행할 때마다 인스턴스 객체 카운터는 하나씩 늘어난다.

반대로 그 인스턴스가 불필요해졌을 때는 release 메시지를 보낸다.

release 는 인스턴스 참조 카운터를 하나씩 줄인다.



-

실제로 인스턴스 객체를 해제하는 건 release 가 아니라 dealloc 메서드이다.

alloc 은 클래스 메서드이지만, dealloc 은 인스턴스 메소드이다.

인스턴스는 release 가 올 때마다 참조 카운터 값을 하나씩 줄인다.

참조 카운터 값이 0 이 된 시점에 dealloc 이 호출되어 인스턴스가 해제된다.


따라서 보통은 dealloc 메서드를 프로그램 속에서 직접 호출하지 않는다.




* 5.2.2. 참조 카운터 확인 프로그램


-

참조 카운터 현재 값을 조사하는 reatinCount 라는 메서드가 있다.

이 메서드는 프로그램 작성에 사용하는 게 아니라 디버그용으로 주로 사용한다.




* 5.2.3. 인스턴스를 해제하는 메서드 정의


-

어떤 클래스의 인스턴스를 해제시킬 때 소유했던 객체의 소유권을 파기하려면 슈퍼 클래스의 dealloc 메서드를 재정의해서 그 속에서 클래스 고유의 인스턴스 변수에 대해 release 메시지를 보내야 한다.




* 5.2.4. 접근자 메서드와 소유권


-

-(void)setMyValue:(id)obj{

     [myValue release];

     myValue = [obj retain];

}


위 코드는 대체로 문제 없이 동작한다.

하지만 인수 obj 가 myValue 와 같은 객체를 참조하고 있으면 문제가 생긴다.



-

Mac OS X 의 Objective-C 컴파일러는 정적인 객체를 지원하지 않는다. ( MyClass a; // 이런 녀석은 불가 )




* 5.2.5. 인스턴스 자동 해제


-

Cocoa 환경의 Objective-C 에는 인스턴스 자동 해제(auto release) 라느 기능이 있다.

release 메시지를 보내고 해제하고 싶은 인스턴스를 적어두는 곳을 메모지라고 편의상 부른다면,

임시 인스턴스를 사용하는 작업이 끝나면 메모지에 적힌 여러 인스턴스에 한꺼번에 release 메시지를 보낸다.

이 메모지의 역할에 해당하는 것이 NSAutoreleasePool 클래스이다.



-

NSAutoreleasePool 인스턴스를 하나 작성한다.

이 인스턴스는 자동 해제 풀이라고 부른다.

자동 해제 풀이 있을 때 임의의 인스턴스 객체에 autorelease 메시지를 보내면 그 인스턴스는 자동 해제 풀에 등록된다.

이 시점에 인스턴스를 해제하거나 참조 카운터 값을 바꾸는 일은 하지 않는다.

그렇지만 autorelease 는 release 와 마찬가지로 소유권을 해제했다고 본다.

그런 다음 작업이 끝나서 자동 해제 풀을 해제시키면 자동 해제 풀에 등록된 모든 인스턴스에 release 메시지가 전달된다.



-

자동 해제 풀에 등록되어 실제로는 소유권이 해제된 객체를 “임시 객체” 라 부를 수 있다.




* 5.2.6. 자동 해제 풀 사용법과 주의점


-

자동 해제 풀의 인스턴스가 없을 때 인스턴스에 autorelease 를 보내면 실행 시 에러가 발생한다.

따라서 보통 자동 해제 풀 기능을 사용하는 프로그램에서는 main() 함수 처음에 자동 해제 풀을 작성해서 종료 바로 직전에 해제한다.

하지만 이 방법으로는 프로그램을 종료할 때만 인스턴스가 해제되므로 실제로는 의미가 없다.



-

자동 해제 풀의 인스턴스는 여러 개 작성할 수 있다.

자동 해제 풀이 여러 개 있으면 가장 최근에 작성된 자동 해제 풀만 유효하다.



-

인스턴스에 retain 메시지를 여러 번 보낼 수 있듯이 autorelease 도 여러 번 보낼 수 있다.

이 때 자동 해제 풀에는 같은 인스턴스가 몇 번이고 중복해서 등록될 수 있는데 retain 횟수와 같다면 문제가 없다.

그러나 자동 해제 풀의 인스턴스 자체에 autorelease 를 보낼 수는 없다.




* 5.2.7. 임시 인스턴스 생성


-

stringWithUTF8String: 과 같은 메소드는 임시 인스턴스(auto release 된)를 생성하기 위한 클래스 메서드이다.

결과로 반환되는 인스턴스는 자동 해제 풀에 등록되어 있다.

alloc 과 초기자를 조합해서 생성할 때와는 다르게 호출하는 쪽 객체가 소유자가 되지 않는다.



-

임시 인스턴스를 간단하게 생성하는 클래스 메서드를 Objective-C 에서는 편의 생성자(convenience constructor)라고 부른다.




* 5.2.8. 실행 반복과 자동 해제 풀


-

GUI 를 사용한 앱은 마우스나 키보드 같은 조작을 감시해서 메뉴나 버튼 클릭, 슬라이더 이동 같은 동작을 검출하면 각각에 대응하는 메서드를 실행시킨다.

이 구조를 실행 반복이라고 부른다. (run loop)



-

실행 반복은 메서드를 실행하기 전에 자동 해제 풀을 작성하고 메서드 처리가 끝난 후에 자동 해제 풀로 해제시킨다.

따라서 Cocoa GUI 를 사용한 프로그램은 자동 해제 풀 관리에 신경 쓰지 않아도 임시 인스턴스를 사용할 수 있다.




* 5.2.9. 해제되지 않는 객체


-

해제하고 싶지 않은 객체도 있을 것이다.

예를 들면 클래스 객체나 NSString 문자열에 있는 객체상수이다.

여기에 retainCount 를 보내면 반환값으로 NSUIntegerMax 가 돌아온다.

이것은 NSUInteger 형의 최대값이다.



-

상수 객체처럼 생성과 해제가 다른 객체를 정의하려면 retain 과 release 메서드 정의를 재정의하는 게 좋을 수 있다.

하지만 ARC 나 가비지 컬렉션에서는 이렇게 구현할 수 없으며 추천하는 방법도 아니다.




cf) const 지정자


-

미리 정해진 값을 가진 데이터가 저장된 배열을 프로그램에서 이용할 때 그 내용이 변경되지 않도록 const 를 사용할 때가 있다.


static const int days[] = { 31, 28, 31, 30, 31, 30 };


이 배열 요소를 가리키는 포인터는 const int *p 처럼 정의할 수 있다.

const 없이 정의된 포인터 int *r 에는 배열 days[] 주소가 대입되지 않으며 컴파일러 경고가 나온다.


그러나 const int *p 는 const 가 아닌 int array 를 담을 수 있다.



-

함수 인수가 포인터일 때 const 가 붙어 있다면 그 포인터가 나타내는 내용은 변경할 수 없다.

C 언어 표준 라이브러리 함수에는 const 가 자주 사용된다.


const 를 사용하면 포인터가 참조하고 있는 곳을 바꿀 수 있는지 없는지를 알 수 있으므로 더 안전하게 프로그램을 작성할 수 있다.

Objective-C 에서도 const 를 자주 사용하는 것이 추천된다.




5.3. 분수 계산기 예제


* 5.3.1. 분수 클래스 Fraction


-

두 양의 정수 사이에서 최대공약수를 구하는 방법은 간단히 유클리드 소거법을 사용할 수 있다.

static int gcd(int a, int b){

     if ( a < b ) {

          return gcd( b, a );

     }

     if ( b == 0 ) {

          return a;

     }


     return gcd( b, a % b );

}



* 5.3.2. 계산 결과를 저장하는 클래스 FracRegister



* 5.3.3. 메인 함수와 실행 예






5.4. ARC 개요


* 5.4.1. ARC 란?


-

ARC(Automatic Reference Counting)란 객체 참조 카운터를 관리하는 코드를 컴파일러가 자동으로 생성하는 구조이다.

컴파일러는 객체를 할당하는 변수가 자동 변수인지, 인스턴스 변수인지 등의 정보를 확인한다.

또한 객체를 반환하는 메서드가 초기자인지 등을 확인한 후 카운터를 바꿀 수 있는 코드를 생성한다.

프로그래머는 retain 이나 release 를 전혀 작성하지 않는다.

따라서 메모리 관리에 신경 쓰는 대신 프로그램 본래의 처리에 집중할 수 있다.



-

ARC 의 메모리 관리 대상은 Objective-C 객체로, malloc() 으로 취득한 메모리 영역 등은 관리하지 않는다.




* 5.4.2. 수동 카운터 조작 금지


-

ARC 를 사용해 컴파일하는 프로그램은 참조 카운터 조작 관련 코드를 작성하면 안 된다.


구체적으로 다음 메서드는 사용해선 안 된다.

     retain

     release

     autorelease

     retainCount


프로그램에서 이런 메서드의 셀렉터(@selector(retain))를 쓰면 안 된다.

그리고 이런 메서드를 정의할 수도 없다.




* 5.4.3. 자동 해제 풀의 새로운 구문


-

NSAutoreleasePool 클래스를 사용할 수 없다.

자동 해제 풀을 설정할 때는 새로 준비된 @autoreleasepool 을 사용한다.



-

지금까지 사용하던 NSAutoreleasePool 을 사용하던 기법은 자동 해제 풀이 설정되어 해제될 때까지 범위 안에서 break, return, goto 같은 걸 사용해서 범위 밖으로 나가면 자동 해제 풀이 해제되지 않은 채 남아 있었는데, 새로운 구문을 사용할 경우 제어가 @autoreleasepool 에서 나가면 해제되므로 블록으로 분기해도 된다.


한편, 이런 새로운 구문은 수동 카운터 관리 방식 프로그램에서도 사용할 수 있다.

하지만 기존 방식보다 더 빠르게 동작하므로 어느 방식이든 상관없이 새로운 구문을 이용하는 게 좋다.




* 5.4.4. 변수 초깃값



* 5.4.5. 메서드 패밀리


-

ARC 에서는 객체가 생성될 때 소유권이 발생한다. alloc init, new 로 시작하는 메서드를 보내 인스턴스를 생성할 때 copy 나 mutableCopy 로 시작하는 메서드를 보내서 인스턴스의 복제본을 생성할 때도 반환되는 객체에 소유권이 발생한다.



-

수동 카운터 관리 방식은 copy, mutableCopy, new 등의 호출을 통해 받아온 객체에 대해

retain 으로 명시적으로 유지하지 않는 한 객체의 소유자가 되지 못한다.

또한 release 나 autorelease 를 사용할 때만 소유권이 해제된다.



-

위와 같은 규칙을 소유권 정책(ownership policy)라고 부른다.

인스턴스 객체의 소유자가 그 인스턴스의 해제를 책임진다는 약속이다.

소유권 정책은 제약이 있는 것이 아니라 관습적으로 사용하는 배려이다.



-

ARC 는 수동 카운터 관리 방식으로 컴파일된 코드와 ARC 를 사용해서 작성된 코드를 섞어 링크할 수 있도록 설계되어 있다.

따라서 어떤 메서드가 객체 생성과 복제와 관련이 있는지를 'init 으로 시작하는 메서드명’ 같은 호칭이 아니라 컴파일러가 명확하게 구별할 수 있는 방법으로 정의해야 한다.

따라서 객체 생성 관련의 특별한 역할이 있는 메서드 그룹을 메서드 패밀리(method family)라는 개념으로 정리한다.



-

메서드가 어떤 메서드 패밀리에 포함되려면 반환값과 메서드 종류 같은 제약을 만족해야 하며 셀렉터가 다음 조건을 만족해야 한다.

그것은 셀렉터의 첫머리에 _가 있으면 그것을 무시하고 셀렉터의 첫 요소가 메서드 패밀리명과 일치하던가 그 이름으로 시작하며 소문자 이외의 문자가 이어져야 한다는 조건이다.


메서드 패밀리명이 init 이라면 다음 셀렉터는 조건을 만족한다.

     init

     initToMemory

     initWithData:

     _init:locale:


다음은 조건을 만족하지 않는다.

     initialize // init 뒤가 소문자

     initwithString:locale: // init 뒤가 소문자

     do_initWithData // init 이 첫 머리에 없다.



-

현재 5종류의 메서드 패밀리가 정의되어 있으며, 메서드 패밀리와 그 제약은 다음과 같다.


alloc 메서드 패밀리

copy 메서드 패밀리

mutableCopy 메서드 패밀리

new 메서드 패밀리

init 메서드 패밀리



-

init 메서드 패밀리 이외에는 클래스 메서드와 인스턴스 메서드의 구별이 없다.

앞서 설명한 것처럼 이런 메서드 패밀리에 속한 메서드 반환값의 객체에는 소유권이 발생한다.

반대로 소유권을 넘길 생각으로 정의한 메서드가 이런 메서드 패밀리에 속하지 않으면 ARC 에서는 의도와는 다르게 처리된다.



-

메서드명으로 의도하지 않은 new 나 copy 로 시작하는 이름을 지을 때가 있다.

예를 들어 줄바꿈을 출력하는 메서드에 newLine 이라는 이름을 붙이고 작성자 정보를 획득하는 메서드를 copyRight 라고 작성할 수도 있다.

하지만 ARC 라면 이런 메서드들이 의도한 대로 동작하는지 어떤지가 컴파일할 때 에러나 경고로 출력되지 않는다.

이런 메서드 패밀리에 일치하는 메서드명을 다른 목적으로는 사용하지 않도록 주의해야 한다.




* 5.4.6. 메서드 dealloc 정의


-

ARC 도 reference count 가 0이 되었을 때 dealloc 을 호출하는 방식으로 객체를 해제하지만 몇 가지 주의사항이 있다.

우선 해제된 객체가 그 인스턴스 변수로 다른 객체를 유지하고 있을 때면 해제(정확히는 카운터를 하나 줄임) 는 ARC 가 자동으로 한다.

따라서 dealloc 에 서술하지 않아도 된다.



-

ARC 는 프로그램 안에서 메서드 dealloc 을 호출하지도 못하고 셀렉터 @selector(dealloc) 를 다루지도 못한다.

컴파일하면 에러가 발생한다.

수동 카운터 관리 방식에서는 프로그래밍 매너로 dealloc 을 직접 호출하지 않는다.

유일하게 슈퍼 클래스의 dealloc 을 호출하는 부분에서 작성할 수 있었지만 이것도 금지사항이다.

ARC 에서 슈퍼 클래스의 dealloc 은 서브 클래스의 dealloc 처리 후에 자동으로 호출된다.




* 5.4.7. ARC 를 사용한 프로그램 컴파일


-

ARC 를 사용한 코드는 gcc 가 아니라 clang 으로 컴파일하므로 -fobjc-arc 옵션을 줘야 한다.

반대로 ARC 를 사용하지 않는다고 명시적으로 알리는 -fno-objc-arc 옵션도 있다.



-

Xcode 로 새로운 프로젝트를 작성할 때 프로젝트의 초기 설정 패널에서 ARC 를 사용할지의 여부를 결정할 수 있다.


"Objective-C Automatic Reference Counting"




* 5.4.8. ARC 기본 사항 정리


-

카운터 조작은 작성하지 않는다.

retain, release, autorelease, retainCount 메서드를 호출하거나 재정의하지 않는다.



-

자동 해제 풀 클래스 NSAutoreleasePool 을 사용하지 않는다.

자동 해제 풀은 새로운 구문인 @autoreleasepool { } 을 사용한다.



-

init, new, copy 로 시작하는 셀렉터를 가진 메서드는 주의해서 정의한다.



-

dealloc 으로 인스턴스 변수를 해제시키지 않아도 된다.

그리고 슈퍼 클래스 dealloc 을 호출할 필요도 없다.



-

컴파일러는 clang 을 사용하고 옵션으로 -fobjc-arc 를 지정한다.




* 5.4.9. 분수 계산 프로그램을 ARC 로 동작시키기


-

Xcode 에는 수동 카운터 관리 방식 프로젝트를 ARC 로 변환해주는 기능이 있다.

Xcode 4.2 에서 메뉴 -> Edit -> Refactor -> Convert to Objective-C ARC… 를 선택한다.

유의해야 할 점은 가비지 컬렉션을 사용한 코드를 ARC 로 변환해주는 기능은 없다.




5.5. 유지 순환과 약한 참조

* 5.5.1. 유지 순환



* 5.5.2. 소유권과 객체의 관계


-

부모에 해당하는 개체를 참조하는 포인터를 백포인터라고 부른다.




* 5.5.3. 약한 참조


-

NSObject __weak *c, *d; // OK

NSObject * __weak e, * __weak f; // f 앞의 weak 은 생략할 수 없다.



-

객체 포인터형에 대한 __weak 와 같은 수식자를 라이프타임 수식자(lifetime qualifier) 또는 소유권 수식자라고 부른다.

라이프타임 수식자는 __strong, __weak, __autoreleasing, __unsafe_unretained 의 네 종류가 있다.




* 5.5.4. 변수 제로화


-

약한 참조 변수는 제로화(zeroing) 기능이 있다.

이건 어떤 인스턴스 객체가 해제되었을 대 그 객체를 참조하던 약한 참조의 변수에 자동으로 빈 포인터 nil 이 대입되는 것이다.

따라서 참조하던 객체가 어느 틈에 해제되더라도 댕글링 포인터가 발생하지 않는다.



-

ARC 를 사용한 프로그래밍에서 제일 주의해야 하는 건 유지 순환이 발생하지 않도록 하는 것이다.

객체 구성에 약한 참조를 넣어서 유지 순환(강한 참조의 순환이라고 한다.)이 발생하지 않도록 하면 객체 해제 미처리나 댕글링 포인터 같은 문제도 방지할 수 있다.

그렇다고 남용해서는 안 된다.



-

다음 코드는 극단적인 예이긴 하지만 객체는 금방 해제된다.

__weak People *ppl = [[People alloc] initWithName:@“Lucy”];




* 5.5.5. 객체 구조의 기본 방침


-

프로그램 속에서 어떤 객체가 인스턴스 변수로 다른 객체를 참조하는 관계를 그림으로 그리면 그 관계는 보통 그래프가 된다.

이런 객체의 집합을 객체 그래프(object graph)라고 부른다.



-

객체 그래프가 되는 길은 유지 순환의 원인이 된다.

따라서 ARC 를 사용할 대 기본적으로 객체 사이의 관계는 트리 구조여야만 한다.



-

자신이 참조하는 객체가 반대로 자신을 참조하는 백 포인터나 객체가 상호 참조하는 관계 또는 트리 구조의 가지를 횡단하는 참조가 필요할 때가 있다.

이런 것이 직접 유지 순환을 일으키는 건 아니지만, 그 가능성이 높아진다.

이런 관계에서 약한 참조를 사용하거나 주의해서 확실하게 관계가 해소되도록 해야 한다. ( 예를 들어 nil 을 대입한다. )




5.6. ARC 프로그래밍의 기타 주의사항

* 5.6.1. 객체를 일반 포인터처럼 취급


-

ARC 를 사용한 프로그램에서 대입된 객체를 유지하지 않은 상태로 제로화도 하지 않은 채 다루고 싶다면 변수에 __unsafe_unretained 라는 라이프타임 수식자를 붙이면 된다.



-

이 __unsafe_unretained 수식어가 붙은 변수는 ‘제로화를 하지 않는 __weak 변수’ 라는 설명도 가능하지만, 일반적인 C 언어 포인터 변수와 같다.



-

ARC 에서 관리되지 않으므로 말 그대로 안전하지 않다.



-

반대로 수동 카운터 관리 방식은 retain 을 쓰지 않은 단순한 대입이 __unsafe_unretained 로 수식된 변수와 같은 동작을 한다.

이런 코드를 ARC 에 대응하도록 바굴 때는 소유권을 재확인해야 한다.



-

ARC 를 사용한 프로그래밍에서 id 형과 void*  형 사이에 자유로운 캐스트가 불가능하다.


포인터를 Core Foundation 으로 다루기 위해 __bridge 라는 형식의 수식자가 있는데, 이걸 쓰면 객체와 void* 형 사이의 캐스트가 가능해진다.




* 5.6.2. 객체를 저장하는 세터 메서드


-

__unsafe_unretained 로 객체 참조할 때와 수동 카운터 관리 방식으로 컴파이로디어 있을지 모르는 모듈을 이용할 때, ARC 에서 문제가 발생할 가능성이 있다.

세터 메서드에 넘긴 객체가 유지되는지는 클래스 인터페이스로는 판별할 수 없다.

객체가 유지되지 않을 대 댕글링 포인터가 발생할 위험이 있다.



-

객체를 값으로 하는 세터 메서드는 넘긴 객체가 불필요해진 시점에 다음처럼 nil 을 넘겨서 댕글링 포인터가 발생할 가능성을 줄여야 한다.

일반적으로 메서드 dealloc 등으로 작성하는 것이 좋다.




* 5.6.3. 메서드 인수를 통해 객체 받기


-

메서드와 함수의 인수로 포인터형을 넘겨서 처리 결과를 해당 포인터를 통해 받는 방법이 있다.

C 프로그래밍에서는 보통 참조 전달(pass-by-reference) 라고 한다.

객체에 사용할 경우 ARC 에서는 라이트백 전달(pass-by-writeback) 이라고 부르며 참조 전달과는 다른 구현 방식을 채용합니다.

전형적인 사용법은 메서드 처리 도중에 에러가 발생했을 때 에러의 종류와 원인을 NSError 클래스의 인스턴스로 호출한 쪽에 돌려주는 것이다.



-

ARC 에서 인스턴스 객체의 간접 포인터(포인터의 포인터)가 메서드 인수로 넘어오면 해당 인수에 __autoreleasing 이라는 라이프타임 수식자가 지정되었다고 처리한다.

따라서 호출한 쪽에 넘겨진 객체는 자동 해제 풀이 유효한 동안은 계속 존재한다.



-

라이트백 전달의 실인수로는 자동 변수 포인터나 nil 만 가능하다.

정적 변수의 포인터나 배열의 첫 부분, 내부를 가리키는 포인터를 넘길 수 없다는 제약이 있다.



-

객체의 간접 포인터를 메서드 인수로 사용할 때 매개변수에 out 형식의 수식자가 달릴 수 있다.

out 수식자는 원래 분산 객체의 호출을 효율적으로 하기 위해 준비된 것이다.

라이트백 전달이면 메서드를 호출할 때와 메서드에서 돌아올 때 임시 변수 사이에 값 복사가 발생한다.

하지만 메서드에서 값을 돌려주기만 하는 역할의 매개변수에 out 을 달면 호출할 때 복사를 생략할 수 있다는 걸 컴파일러에 알릴 수 있다.




* 5.6.4. 객체를 포함한 C 배열


-

ARC 에서 객체 포인터의 포인터는 기본으로 __autoreleasing 으로 해석된다.

__autoreleasing 과 다른 수식자를 사용하면 인수가 라이트백 전달의 포인터가 아니라는 걸 나타낼 수 있다.

명시적으로 __strong 을 쓰면 배열 요소를 바꿔 쓸 수 있으며 const 는 참조 지정이다.



-

인수 형식은 다음의 어떤 방법이라도 가능하다.

(People __strong **)

(People __strong *[])

(People * __strong *)

(People * __strong [])

(__strong People **)

(__strong People *[])




* 5.6.5. 구조체 관련 제약


-

ARC 프로그램에서 C 구조체 또는 공용체에 객체를 저장한 멤버를 포함하면 안 된다는 제약이 있다.

구조체 내부까지 소유권 관리를 할 수 없기 때문이다.



-

구조체 대신 Objective-C 클래스를 정의해서 사용하는 편이 현실적이다.

실행 효율 면에서 구조체를 꼭 사용해야 한다면 __unsafe_unretained 수식자를 사용하는 방법도 있다.

하지만 메모리 관리를 참조 카운터 없이 전부 수동으로 해야만 한다.




* 5.6.6. 컴파일러 지정


-

메서드 패밀리 명명규칙을 다르지 않는 메서드를 계속 사용해 왔다면,

& 이런 메서드명을 바꿀 수 없는 사정이 있다면 컴파일러에 메서드 종류를 명시적으로 알릴 수 있다.


그렇게 하려면 메서드 선언 끝에 다음 중 하나의 매크로를 둔다.


NS_RETURNS_RETAINED

     init 이나 copy 처럼 반환값의 객체를 호출한 쪽이 해제할 책임을 지는 메서드라는 것을 알려준다.


NS_RETURNS_NOT_RETAINED

     메서드 패밀리에 속하지 않는 일반적인 메서드와 마찬가지로 반환값의 객체를 호출한 쪽이 해제하지 않는 메서드라는 걸 알려준다.


하지만 이 방법은 마지막 수단으로 보통은 메서드명을 변경해야 한다.



-

매크로는 컴파일러에 지시하는 어노테이션(annotation) 으로 바꿀 수 있으며, 매크로는 헤더 파일 NSObjCRuntime.h 에 정의되어 있다.

어노테이션은 이 외에도 많지만 언어의 일부가 아닌 컴파일러 특유의 확장 기능이다.


또한 컴파일러가 ARC 기능을 사용하는지 사용하지 않는지에 따라 조건부 컴파일을 하고 싶을 대는 #if 조건부에 __has_feature(objc_arc)를 기입한다.

이 식은 ARC 를 사용하면 1  그렇지 않으면 0 이 된다.





반응형

댓글