ARC有效时,id和void*的显式转换
在ARC有效的情况下编译源代码,必须遵守一定的规则,其中之一就是“id”和“void *”需要显式转换。
在ARC无效时,像下面的代码这样进行id变量和void*变量进行强制转换是没有问题的:
@autoreleasepool { id obj0 = [[NSObject alloc] init]; void* ptr = obj0; id obj1 = ptr; NSLog(@"obj retain count:%zi", [obj1 retainCount]); [obj1 release]; //_objc_autoreleasePoolPrint(); }
但是这部分代码在ARC有效时会引起编译错误:
ARC有效的情况下需要遵循的另一条规则是:不能使用retain/release/retainCount/autorelease。
id类型或者对象型变量赋值给void *或者逆向赋值时(id类型和void*类型相互转换)需要进行显式的转换。如果只是单纯的赋值,可以使用“__bridge”进行显示转换。则上面的代码可以这项修改:
@autoreleasepool { id obj0 = [[NSObject alloc] init]; void* ptr = (__bridge void*)obj0; id obj1 = (__bridge id)ptr; //_objc_autoreleasePoolPrint(); }
但是转换为void *的__bridge转换安全性比较低,如果管理时不注意赋值对象的所有者,很可能会因悬垂指针而导致程序崩溃。
为了补充__bridge转换的不足,还有另外两种转换,分别是“__bridge_retained”转换和“__bridge_transfer”转换。修改后的代码如下(__bridge_retained转换):
id obj0 = [[NSObject alloc] init]; void* ptr = (__bridge_retained void*)obj0;
__bridge_retained转换可使要转换赋值的变量也持有所赋值的对象,在ARC无效时能达到同样效果的代码如下:
id obj0 = [[NSObject alloc] init]; void* ptr = obj0; [(id)ptr retain]; NSLog(@"obj retain count:%zi", [obj0 retainCount]); //obj retain count:2
下面说下__bridge_transfer转换,__bridge_transfer转换提供与__bridge_retained转换相反的操作,被转换的变量所持有的对象在该变量被赋值给转换目标后随之释放。__bridge_transfer转换代码如下:
id obj0 = [[NSObject alloc] init]; void* ptr = (__bridge_retained void *)obj0; id obj1 = (__bridge_transfer id)ptr;
在ARC无效时与__bridge_transfer转换有同样效果的代码如下:
id obj0 = [[NSObject alloc] init]; void* ptr = obj0; //等同__bridge_transfer id obj1 = ptr; [obj1 retain]; [(id)ptr release]; NSLog(@"obj retain count:%zi", [obj0 retainCount]); //obj retain count:1