本文为 《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。选用本身更熟悉的方法便可。