__bridge __bridgeretained __bridgetransfer的区别

iOS开发中,经常会接触到两种对象,Objective-C对象和Core Foundation对象,他们之间有所不同,可以互相转换。最大的不同之处在于,在ARC模式下,前者不用开发者手动管理内存,后者需要开发者手动管理内存,即调用CFRelease方法释放对象,否则会造成内存泄漏。转换的话主要会用到以下3个方法:

  • __bridge,
  • __bridge_retained
  • __bridge_transfer

__bridge可以用于OC对象和CF对象互转,例如

NSObject *obj = [[NSObject alloc] init];    //retain count 1
CFTypeRef cfObj1 = (__bridge CFTypeRef)obj; //retain count 1
NSObject *obj1 = (__bridge id)cfObj1;       //retain count 2

在这种转换方式下,如果是OC对象转换成CF对象,引用计数不变,如果是CF对象转换成OC对象,引用计数会+1。
__bridge_retained用于OC对象转换为CF对象,例如

NSObject *obj = [[NSObject alloc] init];                //retain count 1
CFTypeRef cfObj1 = (__bridge_retained CFTypeRef)obj;    //retain count 2

//等价写法
NSObject *obj = [[NSObject alloc] init];                //retain count 1
CFTypeRef cfObj1 = (CFTypeRef)CFBridgingRetain(obj);    //retain count 2

这种情况下,obj的引用计数会+1,obj的释放不会影响到cfObj1的使用
__bridge_transfer用于CF对象转换为OC对象,例如

NSObject *obj = [[NSObject alloc] init];                //retain count 1
CFTypeRef cfObj1 = (__bridge_retained CFTypeRef)obj;    //retain count 2
NSObject *obj1 = (__bridge_transfer id)cfObj1;          //retain count 2

//等价写法
NSObject *obj = [[NSObject alloc] init];                  //retain count 1
CFTypeRef cfObj1 = (__bridge_retained CFTypeRef)obj;      //retain count 2
NSObject *obj1 = (NSObject *)CFBridgingRelease(cfObj1);   //retain count 2

最后来做个练习,看下以下代码输出什么

NSObject *obj = [[NSObject alloc] init];                //retain count 1
CFTypeRef cfObj = (__bridge_retained CFTypeRef)obj;     //retain count 2
CFTypeRef cfObj1 = (__bridge CFTypeRef)obj;             //retain count 2
CFTypeRef cfObj2 = (__bridge_retained CFTypeRef)obj;    //retain count 3

NSObject *obj1 = (__bridge_transfer id)cfObj1;          //retain count 3
NSObject *obj2 = (__bridge id)cfObj2;                   //retain count 4

NSLog(@"obj retainCount %ld", (long)CFGetRetainCount(cfObj));

NSString *str = [NSString stringWithFormat:@"testStr"];
CFStringRef cfStr = (__bridge CFStringRef)str;
NSLog(@"str retainCount %ld", (long)CFGetRetainCount(cfStr));   //retain count 9223372036854775807 = 0x7FFFFFFFFFFFFFFF

答案是

obj retainCount 4
str retainCount 9223372036854775807

str会被当做是字符串常量,retainCount是一个最大值,保证不会被系统回收。
个人认为苹果的这套API设计得不是很好,比如__bridge,并不是说引用计数不增加,而是看是转换的关系是什么,需要理解记忆一下才行。