__bridge、__bridge_retained 和 __bridge_transfer

本文爲 《Objective-C 高級編程》1.3.4 中的部份內容編程

在 ARC 無效時,像如下代碼這樣將 id 變量強制轉換 void * 變量並不會出問題。安全

/* ARC 無效 */

id obj = [[NSObject alloc] init];

void *p = obj;
複製代碼

更進一步,將該 void * 變量賦值給 id 變量中,調用其實例方法,運行時也不會有問題。函數

/* ARC 無效 */

id o = p;

[o release];
複製代碼

可是在 ARC 有效時這便會引發編譯錯誤。ui

id 型或對象型變量賦值給 void * 或者逆向賦值時都須要進行特定的轉換。若是隻想單純地賦值,則可使用「__bridge 轉換」。spa

id obj = [[NSObject alloc] init];

void *p = (__bridge void *)obj;

id o = (__bridge id)p;
複製代碼

像這樣,經過「__bridge 轉換」,id 和 void * 就可以相互轉換。指針

可是轉換爲 void * 的 __bridge 轉換,其安全性與賦值給 __unsafe_unretained 修飾符相近,甚至會更低。若是管理時不注意賦值對象的全部者,就會因懸垂指針而致使程序崩潰。code

__bridge 轉換中還有另外兩種轉換,分別是「__bridge_retained 轉換」和「__bridge_transfer 轉換」對象

id obj = [[NSObject alloc] init];

void *p = (__bridge_retained void *)obj;
複製代碼

__bridge_retained 轉換可以使要轉換賦值的變量也持有所賦值的對象。下面咱們來看 ARC 無效時源代碼是如何編寫的。作用域

/* ARC 無效 */

id obj = [[NSObject alloc] init];

void *p = obj;

[(id)p retain];
複製代碼

__bridge_retained 轉換變爲了 retain。變量 obj 和變量 p 同時持有對象。再來看幾個其餘的例子。string

void *p = 0;

{
    id obj = [[NSObject alloc] init];

    p = (__bridge_retained void *)obj;
}

NSLog(@"class=%@", [(__bridge id)p class]);
複製代碼

變量做用域結束時,雖然隨着持有強引用的變量 obj 失效,對象隨之釋放,但因爲 __bridge_retained 轉換使變量 p 看上去處於持有該對象的狀態,所以該對象不會被廢棄。下面咱們比較下 ARC 無效時的代碼是怎樣的。

void *p = 0;

{
    id obj = [[NSObject alloc] init];
    /* [obj retainCount] -> 1 */

    p = [obj retain];
    /* [obj retainCount] -> 2 */

    [obj release];
    /* [obj retainCount] -> 1 */
}

/* * [(id)p retainCount] -> 1 即 [obj retainCount] -> 1 對象仍存在 */

NSLog(@"class=%@", [(__bridge id)p class]);
複製代碼

__bridge_transfer 轉換提供與此相反的動做,被轉換的變量所持有的對象在該變量被賦值給轉換目標變量後隨之釋放。

id obj = (__bridge_transfer id)p;
複製代碼

該源代碼在 ARC 無效時又如何表達呢?

/* ARC 無效 */

id obj = (id)p;
[obj retain];
[(id)p release];
複製代碼

同 __bridge_retained 轉換與 retain 相似,__bridge_transfer 轉換與 release 類似。在給 id obj 賦值時 retain 即至關於 __strong 修飾符的變量。

若是使用以上兩種轉換,那麼不使用 id 型或對象型變量也能夠生成、持有以及釋放對象。雖然能夠這樣作,但在 ARC 中不推薦這種方法。使用時還請注意。

固然,也可使用 CFBridgingRetain 函數來代替 __bridge_retained,使用 CFBridgingRelease 來代替 __bridge_transfer。選用本身更熟悉的方法便可。

相關文章
相關標籤/搜索