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

源代碼下載

下載地址:蘋果公開的源代碼在這里可以下載,https://opensource.apple.com/tarballs/

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

當然,如果你想在github上在線查看源代碼,可以點這里:runtimeCoreFoudation

為什么需要了解 引用轉換

例如,在用到runtime的關聯對象API的時候

static NSString * const kCMkvoClassPrefix_for_Block = @"CMObserver_";

NSMutableArray * observers = objc_getAssociatedObject(self, (__bridge void *)kCMkvoAssiociateObserver_for_Block);

需要通過 (__bridge void *) 轉換 id 和 void * 。為什么轉換?這是因為獲取關聯對象的API -- objc_getAssociatedObject 里面的參數要求的。先看一下它的API:

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對象。

至于上面的代碼,完整的功能可查閱 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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容