iOS引用轉換:Foundation與Core Foundation對

local0發表於2021-09-09

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中結構體的原始碼 。

圖片描述

  • 這些結構體的定義如下:

struct __CFArray {    CFRuntimeBase _base;    CFIndex _count;     /* number of objects */
    CFIndex _mutations;
    int32_t _mutInProgress;
    __strong void *_store;           /* can be NULL when MutableDeque */};
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 型別摻雜使用,則可利用兩個框架之間的 “toll-free bridging”。所謂的Toll-free bridging是說您可以在某個框架的方法或函式同時使用 Core Foundation 和 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



作者:陳滿iOS
連結:
來源:簡書


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/132/viewspace-2808898/,如需轉載,請註明出處,否則將追究法律責任。

相關文章