Objective-C) ARC와 Toll-Free Bridging
안녕하세요 :)
오늘은 오랜만에 Objective-C에 대해 다뤄보려고 해요!!!!
제가 퇴사를 하게 되어, 앞으로 Objective-C로 코딩할 일이 많지 않을 것 같아서
까먹지 않기 위해 당분간은 Objective-C에 대한 포스팅도 해보려고 합니다 ㅎㅎㅎ
오늘은 그 중에서 ARC와 Toll-Free Bridging에 대해 다룰 거예요 :D
Toll-Free Bridging.......이라... 언제 들어 봤냐면
Swfit에서 String은 왜 Heap에 저장될까........ 에 대해 고찰할 때
String 과 NSString은 Toll-Free Bridging 된다고 말한 적 있어요!!!!!
오늘은 이에 대해 조금 더 자세하게 (물론 전지적 옵젝씨 관점에서)
메모리 관점에서 살펴보려고 합니다!!!!
모든 포스팅은 편의 말투로 합니다~!!
1. NSType vs CFType
우리가 Objective-C로 개발을 하다보면,
거의 대부분 NS로 시작되는 타입을 사용하지만, 가끔은 CF로 시작되는 타입도 사용하게 됨
이 NS와 CF의 차이점이 무엇이냐면,
NSType |
CFType |
|
특징 |
Core Foundation 범주에 속한 클래스로, NSObject를 상속받고 있다 |
Core Foundation Type으로 C로 구현된 타입으로 클래스가 없다 |
Reference Count |
ARC에 의해 관리된다 |
C함수들과 구조체 타입으로만 구성되어 있기 때문에, CFRetain(), CFRelease()를 수동으로 호출해줘야 한다. |
그러하다
NS는 우리가 잘 알듯 Core Foundation에 속해 있어서
ARC로 설정하면 Reference Counting을 자동으로 해서 retain/release를 해줌!
그러나 CFType은 C언어로 구현되어 있어 구조체 타입 & 함수로 이루어져 있고,
따라서 retain/release를 할 때 직접 함수를 호출해서 수동으로 해줘야 함
이 개념을 알고가야함 :)
2. Toll-Free Bridging
그렇다면, Toll-Free Bridging이 무엇이냐
앞서 살펴본 NSType과 CFType은 서로 비슷한 타입이 존재함
NSArray & CFArray
NSDictionary & CFDictionary
NSStream & CFStream
등등...
이렇게 비슷한 타입들은 서로 간에 타입 캐스팅이 되어,
타입 캐스팅을 통해 서로 왔다 갔다 할 수 있음!!!!
예로 NSArray는 타입 캐스팅을 통해 CFArray로 사용할 수있고,
반대로 CFArray도 타입 캐스팅을 통해 NSArray로 사용할 수 있음!!
이렇게 타입 캐스팅 되는 것을
Toll-Free Bridging
이라고 부르는데,
Toll-Free라고 부르는 이유는 서로 다른 Type이지만,
아무런 비용 없이 캐스팅할 수 있다 하여 Toll-Free가 붙은 것임 ㅎㅎ
Toll-Free Bridging을 하는 방법은 간단함
(__bridge Type *)
|
보통 일반적으로 이런 식으로 함 ㅎㅎㅎㅎ
CFString을 NSString으로 Toll-Free Bridging 한다고 하면
CFStringRef cfStr = CFSTR("Sodeul");
NSString *nsStr = (__bridge NSString *)(cfStr);
|
이런 식으로 사용해주면 됨!!!
3. Toll-Free Bridging 한 경우, Reference Count는 누구 것을 따르나요?
앞서 말했듯 NSType과 CFType은 서로 Reference Count를 관리하는 방법이 달랐음
따라서 이렇게 Toll-Free Bridging을 할 경우,
ARC가 메모리를 해제해 주느냐, CF 방식대로 메모리를 해제해 주느냐
라는 문제점이 있음!!!!
이때, 이 메모리 관리를 어떻게 할 것이냐 때문에 Toll-Free Bridging을 선언하는 방식은
위에서 알려준 방식 외에도 두 가지가 더 존재하는데,
먼저,
NSString <-> CFString을 예제로 들면서
위에서 설알려준 가장 흔하게 사용하는 방식부터 설명해 주겠음
3-1. __ bridge
(__bridge Type *)
|
위에서 알려준 가장 흔하게 사용하는 이 방식은,
메모리 해제 책임을 어떻게 가지는지 설명해보겠음 :)
① NSType ▶ CFType
ARC가 메모리 해지 책임을 갖는다
NSString *nsStr = @"Sodeul";
CFStringRef cfStr = (__bridge CFStringRef)(nsStr);
|
이런 경우엔
ARC가 메모리 해지 책임을 갖기 때문에, CFRelease 함수를 호출해주지 않아도 됨
(우리가 평소 작성 하듯이 ARC가 자동으로 해제해줌)
② CFType ▶ NSType
Core Foundation 방법으로 수동으로 메모리를 해제해줘야 한다
CFStringRef cfStr = CFSTR("Sodeul");
NSString *nsStr = (__bridge NSString *)(cfStr);
|
이런 경우엔
Core Foundation 방식으로 메모리를 해제 해줘야 하고,
따라서 다 사용하고 난 뒤에는
CFRelease(cfStr);
|
이런 식으로 CFRelease 함수를 호출해주어야 함 :))
3-2. __bridge_transfer
NSType ▶ CFType 인 경우에만 사용한다
메모리 관리 책임을 ARC에게 넘겨버린다
NSString *nsStr = @"Sodeul";
CFStringRef cfStr = (__bridge_transfer CFStringRef)(nsStr);
|
이 __bridge_transfer은 CFBridgingRelease라는 함수로 대체해서 쓸 수 있음!!!
CFStringRef cfStr = CFSTR("Sodeul");
NSString *nsStr = CFBridgingRelease(cfStr);
|
이렇게 사용 가능 ㅎㅎㅎㅎㅎㅎ
마찬가지로 ARC에게 넘기기 떄문에 CFRelease() 함수를 호출하지 않음
3-3. __bridge_retain
CFType ▶ NSType 인 경우에만 사용한다
Core Foundation 방법으로 수동으로 메모리를 해제해줘야 한다
CFStringRef cfStr = CFSTR("Sodeul");
NSString *nsStr = (__bridge NSString *)(cfStr);
CFRelease(cfStr);
|
이 __bridge_retain은 CFBridgingRetain 함수로 대체해서 쓸 수 있음
CFStringRef cfStr = CFSTR("Sodeul");
NSString *nsStr = CFBridgingRetain(cfStr);
CFRelease(cfStr);
|
이렇게 사용함 ㅎㅎㅎㅎㅎ
마찬가지로 다 사용하고 난 후엔 CFRelease() 함수를 호출해줘야 함
.
.
.
흠...... 그냥 __bridge 쓰면 되는 겅 아닌가...?
__bridge_retain + __bridge_transfer 합친게 __bridge 아님..?
이 부분에 대해선 더 공부하고
뭐가 다른지, 같은 건지 알게되면 추가 하겠음 😂
썸네이 없길래 투척ㅋ