iOS引用轉換:Foundation與Core Foundation對象互相轉換(__CFString轉NSString,void *轉id等等)

源代碼下載html

下載地址:蘋果公開的源代碼在這裏能夠下載,opensource.apple.com/tarballs/git

例如,其中,有兩個比較常見須要學習源碼的下載地址:github

固然,若是你想在github上在線查看源代碼,能夠點這裏:runtimeCoreFoudation數組

爲何須要了解 引用轉換bash

例如,在用到runtime的關聯對象API的時候,可能見到過這種代碼:數據結構

static NSString * const kCMkvoClassPrefix_for_Block = @"CMObserver_";

NSMutableArray * observers = objc_getAssociatedObject(self, (__bridge void *)kCMkvoAssiociateObserver_for_Block);
複製代碼

須要經過 (__bridge void *) 轉換 id 和 void * 。爲何轉換?這是由於objc_getAssociatedObject的參數要求的。先看一下它的API:app

objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
複製代碼

對比一下兩個參數:框架

  • const void * _Nonnull key
  • static NSString * const kCMkvoClassPrefix_for_Block = @"CMObserver_";

那麼,想把NSString的字符串轉成void *類型參數,必須進行引用轉換。那麼轉換的是什麼?OC中常常要對兩個框架的對象進行轉換:Foundation與Core Foundation對象。iphone

至於上面的代碼,完整的功能可查閱 iOS開發·KVO用法,原理與底層實現: runtime模擬實現KVO監聽機制函數

1. 兩個框架的基本知識

1.1 Foundation

框架名是Foundation.framework,在Xcode新建工程時能夠選擇導入(其實會默認自動依賴好)。Foundation框架容許使用一些基本對象,如數字和字符串,以及一些對象集合,如數組,字典和集合,其餘功能包括處理日期和時間、內存管理、處理文件系統、存儲(或歸檔)對象、處理幾何數據結構(如點和長方形)。這個框架中的類都是一些最基礎的類。來自於這個框架的類名以NS開頭。

Foundation框架提供了很是多好用的類, 好比:

NSString : 字符串
NSArray : 數組
NSDictionary : 字典
NSDate : 日期
NSData : 數據
NSNumber : 數字
複製代碼

1.2 Core Foundation

Core Foundation 對象主要使用在用C語言編寫的Core Foundation 框架中,並使用引用計數的對象。在ARC無效時,Core Foundation 框架中的retain/release 分別是 CFRetain /CFRelease。

框架CoreFoundation.framework是一組C語言接口,它們爲iOS應用程序提供基本數據管理和服務功能。下面列舉該框架支持進行管理的數據以及可提供的服務。查閱Core Foundation的完整API 點這裏

  • CF的引用定義:CFStringRefCFArrayRef 查閱CFArrayRef 的定義 點這裏 查閱CFStringRef 的定義 點這裏
typedef const struct __CFString * CFStringRef;
typedef const struct __CFArray * CFArrayRef;
複製代碼
  • CF的源代碼:__CFString__CFArray 查閱CF中結構體的源代碼 點這裏

  • 這些結構體的定義以下:
CFArray.c
struct __CFArray {
    CFRuntimeBase _base;
    CFIndex _count;		/* number of objects */
    CFIndex _mutations;
    int32_t _mutInProgress;
    __strong void *_store;           /* can be NULL when MutableDeque */
};
複製代碼
CFString.c
struct __CFString {
    CFRuntimeBase base;
    union {	// In many cases the allocated structs are smaller than these
	struct __inline1 {
	    CFIndex length;
        } inline1;                                      // Bytes follow the length
	struct __notInlineImmutable1 {
	    void *buffer;                               // Note that the buffer is in the same place for all non-inline variants of CFString
	    CFIndex length;                             
	    CFAllocatorRef contentsDeallocator;		// Optional; just the dealloc func is used
	} notInlineImmutable1;                          // This is the usual not-inline immutable CFString
	struct __notInlineImmutable2 {
	    void *buffer;
	    CFAllocatorRef contentsDeallocator;		// Optional; just the dealloc func is used
	} notInlineImmutable2;                          // This is the not-inline immutable CFString when length is stored with the contents (first byte)
	struct __notInlineMutable notInlineMutable;
    } variants;
};
複製代碼

1.3 二者關係

Core Foundation 框架和 Foundation 框架緊密相關,它們爲相同功能提供接口,但 Foundation 框架提供Objective-C接口。Foundation對象 和 Core Foundation對象間的轉換,俗稱爲橋接。若是您將Foundation 對象和 Core Foundation 類型摻雜使用,則可利用兩個框架之間的 「 Toll Free Bridging」。所謂的Toll-free bridging是說您能夠在某個框架的方法或函數同時使用 Core Foundation 和 Foundation 框架中的某些類型。

不少數據類型支持這一特性,其中包括羣體和字符串數據類型。每一個框架的類和類型描述都會對某個對象是否爲 Toll-free bridged,應和什麼對象橋接進行說明。如需進一步信息,請閱讀 Core Foundation 框架參考

2. Objective-C指針與CoreFoundation指針之間的轉換

2.1 MRC下的轉換

  • CF-->OC 強制轉換符:(CFStringRef)
  • OC-->CF 強制轉換符:(NSString *)
  • 例子
-(void)bridgeInMRC {
    // 將Foundation對象轉換爲Core Foundation對象,直接強制類型轉換便可
    NSString *strOC1 = [NSString stringWithFormat:@"xxxxxx"];
    CFStringRef strC1 = (CFStringRef)strOC1;
    NSLog(@"%@ %@", strOC1, strC1);
    [strOC1 release];
    CFRelease(strC1);
    
    // 將Core Foundation對象轉換爲Foundation對象,直接強制類型轉換便可
    CFStringRef strC2 = CFStringCreateWithCString(CFAllocatorGetDefault(), "12345678", kCFStringEncodingASCII);
    NSString *strOC2 = (NSString *)strC2;
    NSLog(@"%@ %@", strOC2, strC2);
    [strOC2 release];
    CFRelease(strC2);
}
複製代碼

2.2 ARC下的轉換

ARC僅管理Objective-C指針(retain、release、autorelease),無論理CoreFoundation指針。CF指針由人工管理,手動的CFRetain和CFRelease來管理。

在ARC中,CF和OC之間的轉化橋樑是 __bridge,有3種方式:

  • __bridge 只作類型轉換,不改變對象全部權,是咱們最經常使用的轉換符。
  • __bridge_transfer:ARC接管 管理內存
  • __bridge_retained:ARC釋放 內存管理

2.3 簡單互相轉換:__bridge

① 從OC轉CF,ARC管理內存:

  • (__bridge CFStringRef)
  • 須要人工CFRetain,不然,Cocoa指針釋放後, 傳出去的指針則無效。
  • 例子
- (void)viewDidLoad  
{  
    [super viewDidLoad];  
      
    NSString *aNSString = [[NSString alloc]initWithFormat:@"test"];  
    CFStringRef aCFString = (__bridge CFStringRef)aNSString;  
      
    (void)aCFString;  
}
複製代碼

上面只是單純地執行了類型轉換,沒有進行全部權的轉移,也就是說,當aNSString對象被ARC釋放的時候,aCFString也不能被使用了。

② 從CF轉OC,須要開發者手動釋放,不歸ARC管:

  • (__bridge NSString *)
  • 須要人工CFRelease,不然,OC對象的指針釋放後,對象引用計數仍爲1,不會被銷燬。
  • 例子
- (void)viewDidLoad  
{  
    [super viewDidLoad];  
      
    CFStringRef aCFString = CFStringCreateWithCString(NULL, "test", kCFStringEncodingASCII);  
    NSString *aNSString = (__bridge NSString *)aCFString;  
      
    (void)aNSString;  
      
    CFRelease(aCFString);  
}  
複製代碼

3. ARC下內存管理髮生改變的轉換

3.1 CF-->OC:__bridge_transfer

  • 例子
- (void)viewDidLoad  {  
    [super viewDidLoad];  
      
    NSString *aNSString = [[NSString alloc]initWithFormat:@"test"];  
    CFStringRef aCFString = (__bridge_retained CFStringRef) aNSString;  
    aNSString = (__bridge_transfer NSString *)aCFString;  
}  
複製代碼

3.2 OC-->CF:__bridge_retained

  • 例子
- (void)viewDidLoad  {  
    [super viewDidLoad];  
      
    NSString *aNSString = [[NSString alloc]initWithFormat:@"test"];  
    CFStringRef aCFString = (__bridge_retained CFStringRef) aNSString;  
      
    (void)aCFString;  
      
    //這時候,即便開啓ARC,也須要手動執行CFRelease  
    CFRelease(aCFString);   
}  
複製代碼

3.3 怎麼區分記憶?

由於ARC沒法管理CF對象的指針,因此,不管是CF轉OC仍是OC轉CF,咱們只需關心CF對象的引用須要加1仍是減1便可。

  • CF轉OC:CFRef必須減1

這樣原來的CF對象就被釋放,因此,之後也不用手動釋放。

NSString   *c = (__bridge_transfer NSString*)my_cfref; // -1 on the CFRef   
複製代碼
  • OC轉CF:CFRef 必須加1

這樣新的CF對象就不會被釋放,因此,之後用完必須手動釋放。

CFStringRef d = (__bridge_retained CFStringRef)my_id;  // returned CFRef is +1  
複製代碼
//這時候,即便開啓ARC,CF對象用完後也須要手動執行CFRelease  
CFRelease(aCFString); 
複製代碼

3.4 轉換相關的宏

  • CFBridgingRetain
NS_INLINE CFTypeRef CFBridgingRetain(id X) {   
    return (__bridge_retain CFTypeRef)X;   
}   
複製代碼
  • CFBridgingRelease
NS_INLINE id CFBridgingRelease(CFTypeRef X) {   
    return (__bridge_transfer id)X;   
} 
複製代碼

例1

下面兩個等效

CFStringRef cfStr = (__bridge_retained CFStringRef)ocStr;  
複製代碼
CFStringRef cfStr = CFBridgingRetain(ocStr);  
複製代碼

例2

下面兩個等效

NSString *ocStr = (__bridge_transfer NSString*)cfStr;
複製代碼
NSString *ocStr = CFBridgingRelease(cfStr);
複製代碼

3.5 總結

  • CF轉化爲OC時,而且對象的全部者發生改變,則使用CFBridgingRelease()__bridge_transfer
  • OC轉化爲CF時,而且對象的全部者發生改變,則使用CFBridgingRetain()__bridge_retained
相關文章
相關標籤/搜索