一个成功的商人是虎,一个失败的商人是狼,你和他们中心哪个打交道??
像那闪烁的微光,欲望把我人生的路子照亮;夜色愈浓,它愈放射出刺眼的光线。自 Xcode4.2 开端导入ARC机制后,为了支撑对象间的转型,Apple又增长了很多转型用的关键字。这一讲我们就来懂得其用法,以及产生的来由。
引子
我们先来看一下ARC无效的时辰,我们写id类型转void*类型的写法:
1 2 |
id obj = [[NSObject alloc] init]; void *p = obj; |
反过来,当把void*对象变回id类型时,只是简单地如下来写,
1 2 |
id obj = p; [obj release]; |
然则上方的代码在ARC有效时,就有了下面的错误:
error: implicit conversion of an Objective-C pointer to ’void *’ is disallowed with ARC void *p = obj; ^ error: implicit conversion of a non-Objective-C pointer type ’void *’ to ’id’ is disallowed with ARC id o = p; ^
__bridge
为懂得决这一题目,我们应用?__bridge?关键字来实现id类型与void*类型的彼此转换。看下面的例子。
1 2 3 4 5 |
id obj = [[NSObject alloc] init]; void *p = (__bridge void *)obj; id o = (__bridge id)p; |
将Objective-C的对象类型用 __bridge 转换为 void* 类型和应用 __unsafe_unretained 关键字润饰的变量是一样的。被代入对象的所有者须要明白对象生命周期的经管,不要呈现异常接见的题目。
除过 __bridge 以外,还有两个 __bridge 相干的类型转换关键字:
- __bridge_retained
- __bridge_transfer
接下来,我们将看看这两个关键字的差别。
__bridge_retained
先来看应用 __bridge_retained 关键字的例子法度:
1 2 3 |
id obj = [[NSObject alloc] init]; void *p = (__bridge_retained void *)obj; |
从名字上我们应当能懂得其意义:类型被转换时,其对象的所有权也将被变换后变量所持有。若是不是ARC代码,类似下面的实现:
1 2 3 4 |
id obj = [[NSObject alloc] init]; void *p = obj; [(id)p retain]; |
可以用一个实际的例子验证,对象所有权是否被持有。
1 2 3 4 5 6 7 8 |
void *p = 0; { id obj = [[NSObject alloc] init]; p = (__bridge_retained void *)obj; } NSLog(@"class=%@", [(__bridge id)p class]); |
出了大括号的局限后,p 仍然指向一个有效的实体。申明他拥有该对象的所有权,该对象没有因为出其定义局限而被烧毁。
__bridge_transfer
相反,当想把底本拥有对象所有权的变量,在类型转换后,让其开释本来所有权的时辰,须要应用 __bridge_transfer 关键字。文字有点绕口,我们还是来看一段代码吧。
若是ARC无效的时辰,我们可能须要写下面的代码。
1 2 3 4 |
// p 变量本来持有对象的所有权 id obj = (id)p; [obj retain]; [(id)p release]; |
那么ARC有效后,我们可以用下面的代码来调换:
1 2 |
// p 变量本来持有对象的所有权 id obj = (__bridge_transfer id)p; |
可以看出来,__bridge_retained 是编译器替我们做了 retain 操纵,而 __bridge_transfer 是替我们做了 release1。
Toll-Free bridged
在iOS世界,首要有两种对象:Objective-C 对象和 Core Foundation 对象0。Core Foundation 对象主如果有C说话实现的 Core Foundation Framework 的对象,此中也有对象引用计数的概念,只是不是 Cocoa Framework::Foundation Framework 的 retain/release,而是自身的 CFRetain/CFRelease 接口。
这两种对象间可以互相转换和操纵,不应用ARC的时辰,纯真的用C原因的类型转换,不须要消费CPU的资料,所以叫做 Toll-Free bridged。比如 NSArray和CFArrayRef, NSString和CFStringRef,他们固然属于不合的 Framework,然则具有雷同的对象布局,所以可以用标准C的类型转换。
比如不应用ARC时,我们用下面的代码:
1 2 |
NSString *string = [NSString stringWithFormat:...]; CFStringRef cfString = (CFStringRef)string; |
同样,Core Foundation类型向Objective-C类型转换时,也是简单地用标准C的类型转换即可。
然则在ARC有效的景象下,将呈现类似下面的编译错误:
Cast of Objective-C pointer type ‘NSString *’ to C pointer type ‘CFStringRef’ (aka ‘const struct __CFString *’) requires a bridged cast Use __bridge to convert directly (no change in ownership) Use __bridge_retained to make an ARC object available as a +1 ‘CFStringRef’ (aka ‘const struct __CFString *’)
错误中已经提示了我们须要如何做:用 __bridge 或者 __bridge_retained 来转型,其差别就是变革对象的所有权。
正因为Objective-C是ARC经管的对象,而Core Foundation不是ARC经管的对象,所以才要特地如许转换,这与id类型向void*转换是一个概念。也就是说,当这两种类型(有ARC经管,没有ARC经管)在转换时,须要告诉编译器如何处理惩罚对象的所有权。
上方的例子,应用 __bridge/__bridge_retained 后的代码如下:
__bridge
1 2 |
NSString *string = [NSString stringWithFormat:...]; CFStringRef cfString = (__bridge CFStringRef)string; |
只是纯真地履行了类型转换,没有进行所有权的转移,也就是说,当string对象被开释的时辰,cfString也不克不及被应用了。
__bridge_retained
1 2 3 4 |
NSString *string = [NSString stringWithFormat:...]; CFStringRef cfString = (__bridge_retained CFStringRef)string; ... CFRelease(cfString); // 因为Core Foundation的对象不属于ARC的经管局限,所以须要本身release |
应用 __bridge_retained 可以经由过程转换目标处(cfString)的 retain 处理惩罚,来使所有权转移。即使 string 变量被开释,cfString 还是可以运器具体的对象。只是有一点,因为Core Foundation的对象不属于ARC的经管局限,所以须要本身release。
实际上,Core Foundation 内部,为了实现Core Foundation对象类型与Objective-C对象类型的彼此转换,供给了下面的函数。
1 2 3 4 5 6 7 |
CFTypeRef CFBridgingRetain(id X) { return (__bridge_retained CFTypeRef)X; } id CFBridgingRelease(CFTypeRef X) { return (__bridge_transfer id)X; } |
所以,可以用 CFBridgingRetain 调换 __bridge_retained 关键字:
1 2 3 4 |
NSString *string = [NSString stringWithFormat:...]; CFStringRef cfString = CFBridgingRetain(string); ... CFRelease(cfString); // 因为Core Foundation不在ARC经管局限内,所以须要主动release。 |
所有权被转移的同时,被转换变量将落空对象的所有权。当Core Foundation对象类型向Objective-C对象类型转换的时辰,会经常用到 __bridge_transfer 关键字。
1 2 3 4 |
CFStringRef cfString = CFStringCreate...(); NSString *string = (__bridge_transfer NSString *)cfString; // CFRelease(cfString); 因为已经用 __bridge_transfer 转移了对象的所有权,所以不须要调用 release |
同样,我们可以应用 CFBridgingRelease() 来庖代 __bridge_transfer 关键字。
1 2 |
CFStringRef cfString = CFStringCreate...(); NSString *string = CFBridgingRelease(cfString); |
总结
由上方的进修我们懂得到 ARC 中类型转换的用法,那么我们实际应用中遵守如何的原则或者办法来区分应用呢,下面我总结了几点关键要素。
- 明白被转换类型是否是 ARC 经管的对象
- Core Foundation 对象类型不在 ARC 经管局限内
- Cocoa Framework::Foundation 对象类型(即一般应用到的Objectie-C对象类型)在 ARC 的经管局限内
- 若是不在 ARC 经管局限内的对象,那么要清楚 release 的义务应当是谁
- 各类对象的生命周期是如何的