ARC中__bridge, __bridge__transfer, __bridge_retained 关系
2014-07-11 15:49
302 查看
总结于 IOS Tuturial 中 ARC两章,详细在dropbox pdf文档。
Toll-FreeBridging
当你在Objective-C和CoreFoundation
对象之间进行转换时,就需要使用Bridgecast。
今天的多数应用很少需要使用CoreFoundation,大多数工作都可以直接使
用Objective-C类来完成。但是某些底层API,如Core
Graphics 和CoreText,
都基于CoreFoundation,而且不太可能会有 Objective-C
的版本。幸运的是,iOS的设计使得这两种类型的对象非常容易转换。
例如NSString和CFStringRef就可以同等对待,在任何地方都可以互换使用,背后的设计就是
toll-freebridging。在 ARC
之前,只需要使用一个简单的强制类型转换即可:
当然,alloc 分配了NSString对象,你需要在使用完之后进行释放,注意是释放转换后的
CFStringRef对象:
CFRelease(s1);
反过来,从CoreFoundation
到Objective-C的方向也类似:
现在我们使用了ARC,情况变得不一样!以下代码在手动内存管理中是完全合法的,但在ARC中却存在问题:
-(NSString *)escape:(NSString *)text
{
CFStringRef s1 =(CFStringRef) [[NSString alloc]
initWithFormat:@"Hello,%@!", name];
CFStringRef s2 =CFStringCreateWithCString(kCFAllocatorDefault, bytes,
kCFStringEncodingMacRoman);
NSString *s3 =(NSString *)s2;
//release the object when you're done
return [(NSString*)CFURLCreateStringByAddingPercentEscapes(
NULL,
NULL,
(CFStringRef)@"!*'();:@&=+$,/?%#[]",//
这里不需要bridging
casts,因为这是一个常量,不需要释放!
CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncodin
g))
}
首先需要移除autorelease调用。然后编译器还会报两个类型转换错误:
Cast ofC pointer type 'CFStringRef' to Objective-C pointer type 'NSString*' requires a bridged cast
Cast ofObjective-C pointer type 'NSString *' to C pointer type'CFStringRef' requires a bridged cast
编译器必须知道由谁来负责释放转换后的对象,如果你把一个NSObject当作
CoreFoundation
对象来使用,则ARC将不再负责释放该对象。但你必须明确地告诉
ARC你的这个意图,编译器没办法自己做主。同样如果你创建一个CoreFoundation
对象并把它转换为NSObject对象,你也必须告诉ARC占据对象的所有权,并在适当的时候释放该对象。这就是所谓的
bridgingcasts。
CFURLCreateStringByAddingPercentEscapes()函数的参数需要两个CFStringRef
对象,其中常量NSString可以直接转换,因为不需要进行对象释放;但是
text参数不一样,它是传递进来的一个NSString对象。而函数参数和局部变量一样,都是
strong指针,这种对象在函数入口处会被retain,并且对象会持续存在直到指针被销毁(这里也就是函数返回时)。
对于text参数,我们希望ARC保持这个变量的所有权,同时又希望临时将它当作
CFStringRef对象来使用。这种情况下可以使用__bridge说明符,它告 诉
ARC不要更改对象的所有权,按普通规则释放该对象即可。
多数情况下,Objective-C对象和
CoreFoundation
对象之间互相转换时,我们都应该使用__bridge。但是有时候我们确实需要给予
ARC某个对象的所有权,或者解除
ARC对某个对象的所有权。这种情况下我们就需要使用另外两种bridgingcasts:
__bridge_transfer:给予ARC
所有权
__bridge_retained:解除ARC
所有权
在上面代码中,"return(NSString*)CFURLCreateStringByAddingPercentEscapes",编译器弹出的修复提示有两个:
两个解决办法:__bridge和
__bridge_transfer,正确的选择应该是__bridge_transfer。
因为CFURLCreateStringByAddingPercentEscapes()函数创建了一个新 的CFStringRef对象,当然我们要的是NSString对象,因此我们使用了强制转换。实际上我们真正想要做的是:
从CFURLCreateStringByAddingPercentEscapes函数的create可以看出,函数会返回一个
retain过的对象。某个人需要负责在适当的时候释放该对象,如果我们不把这个对象返回为
NSString,则通常我们需要自己调用:
{
}
不过ARC只能作用于Objective-C对象,不能释放CoreFoundation
对象。因此这里你仍然需要调用
CFRelease()来释放该对象。
这里我们的真实意图是:转换新创建的CFStringRef对象为NSString对象,并且当我们不再需要使用这个
NSString对象时,ARC能够适当地释放它。
因此我们使用
__bridge_transfer告诉ARC:"嘿!ARC,这个CFStringRef对象现在是一个NSString对象了,我希望你来销毁它,我这里就不调用CFRelease()来释放它了"。
如果我们使用__bridge,就会导致内存泄漏。ARC并不知道自己应该在使用完对象之后释放该对象,也没有人调用CFRelease()。结果这个对象就会永远保留在内存中。因此选择正确的bridge说明符是至关重要的。
为了代码更加可读和容易理解,iOS还提供了一个辅助函数:
CFBridgingRelease()。函数所做事情和__bridge_transfer
强制转换完全一样,但更加简洁和清晰。CFBridgingRelease() 函数定义为内联函数,因此不会导致额外的开销。函数之所以命名为
CFBridgingRelease(),是因为一般你会在需要使用CFRelease()释放对象的地方,调用CFBridgingRelease()来传递对象
的所有权。
- (NSString *)escape:(NSString *)text
{
}
另一个常见的需要CFBridgingRelease的情况是AddressBookframework:
{
}
只要你调用命名为Create,Copy, Retain
的CoreFoundation
函数,你都 需要使用CFBridgingRelease()安全地将值传递给ARC。
__bridge_retained则正好相反,假设你有一个NSString对象,并且要将它传递给某个
Core FoundationAPI,该函数希望接收这个 string
对象的所有权。这时候你就不希望
ARC也去释放该对象,否则就会对同一对象释放两次,而且必将导致应用崩溃!换句话说,使用
__bridge_retained将对象的所有权给 予CoreFoundation,而 ARC
不再负责释放该对象。
如下面例子所示:
returnCFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
NULL,
(__bridgeCFStringRef)text,
NULL,
CFSTR("!*'();:@&=+$,/?%#[]"),
CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncodin
g)));
returnCFBridgingRelease(ABRecordCopyCompositeName(...));
NSString *s1 =[[NSString alloc] initWithFormat:@"Hello, %@!", name];
CFStringRef s2 =(__bridge_retained CFStringRef)s1;
//. . .
一旦(__bridge_retainedCFStringRef)
转换完成,ARC 就不再负责释放该对象。如果你在这里使用
__bridge,应用就很可能会崩溃。ARC可能在CoreFoundation
正在使用该对象时,释放掉它。
同样__bridge_retained也有一个辅助函数:CFBridgingRetain()。从名字 就可以看出,这个函数会让
CoreFoundation
执行retain,实际如下:
现在你应该明白了,上面例子的CFRelease()是和CFBridgingRetain()对应
的。你应该很少需要使用__bridge_retained或
CFBridgingRetain()。
__bridge转换不仅仅局限于CoreFoundation
对象,某些API使用void*
指针作为参数,允许你传递任何东西的引用:Objective-C对象、Core
Foundation对象、malloc()内存缓冲区等等。void*表示这是一个指针,但实际的数据类型可以是任何东西!
要将Objective-C对象和void*互相转换,你也需要使用__bridge
转换, 如下:
在animationdelegate
方法中,你再将对象强制转回来:
{
}
总结:
使用CFBridgingRelease(),从Core Foundation
传递所有权给Objective-C;
使用CFBridgingRetain(),从Objective-C
传递所有权给CoreFoundation;
使用__brideg,表示临时使用某种类型,不改变对象的所有权。
Toll-FreeBridging
当你在Objective-C和CoreFoundation
对象之间进行转换时,就需要使用Bridgecast。
今天的多数应用很少需要使用CoreFoundation,大多数工作都可以直接使
用Objective-C类来完成。但是某些底层API,如Core
Graphics 和CoreText,
都基于CoreFoundation,而且不太可能会有 Objective-C
的版本。幸运的是,iOS的设计使得这两种类型的对象非常容易转换。
例如NSString和CFStringRef就可以同等对待,在任何地方都可以互换使用,背后的设计就是
toll-freebridging。在 ARC
之前,只需要使用一个简单的强制类型转换即可:
当然,alloc 分配了NSString对象,你需要在使用完之后进行释放,注意是释放转换后的
CFStringRef对象:
CFRelease(s1);
反过来,从CoreFoundation
到Objective-C的方向也类似:
现在我们使用了ARC,情况变得不一样!以下代码在手动内存管理中是完全合法的,但在ARC中却存在问题:
-(NSString *)escape:(NSString *)text
{
CFStringRef s1 =(CFStringRef) [[NSString alloc]
initWithFormat:@"Hello,%@!", name];
CFStringRef s2 =CFStringCreateWithCString(kCFAllocatorDefault, bytes,
kCFStringEncodingMacRoman);
NSString *s3 =(NSString *)s2;
//release the object when you're done
[s3 release];
return [(NSString*)CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)text,
NULL,
(CFStringRef)@"!*'();:@&=+$,/?%#[]",//
这里不需要bridging
casts,因为这是一个常量,不需要释放!
CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncodin
g))
autorelease];
}
首先需要移除autorelease调用。然后编译器还会报两个类型转换错误:
Cast ofC pointer type 'CFStringRef' to Objective-C pointer type 'NSString*' requires a bridged cast
Cast ofObjective-C pointer type 'NSString *' to C pointer type'CFStringRef' requires a bridged cast
错误分别来自以下两行代码:
编译器必须知道由谁来负责释放转换后的对象,如果你把一个NSObject当作
CoreFoundation
对象来使用,则ARC将不再负责释放该对象。但你必须明确地告诉
ARC你的这个意图,编译器没办法自己做主。同样如果你创建一个CoreFoundation
对象并把它转换为NSObject对象,你也必须告诉ARC占据对象的所有权,并在适当的时候释放该对象。这就是所谓的
bridgingcasts。
CFURLCreateStringByAddingPercentEscapes()函数的参数需要两个CFStringRef
对象,其中常量NSString可以直接转换,因为不需要进行对象释放;但是
text参数不一样,它是传递进来的一个NSString对象。而函数参数和局部变量一样,都是
strong指针,这种对象在函数入口处会被retain,并且对象会持续存在直到指针被销毁(这里也就是函数返回时)。
对于text参数,我们希望ARC保持这个变量的所有权,同时又希望临时将它当作
CFStringRef对象来使用。这种情况下可以使用__bridge说明符,它告 诉
ARC不要更改对象的所有权,按普通规则释放该对象即可。
多数情况下,Objective-C对象和
CoreFoundation
对象之间互相转换时,我们都应该使用__bridge。但是有时候我们确实需要给予
ARC某个对象的所有权,或者解除
ARC对某个对象的所有权。这种情况下我们就需要使用另外两种bridgingcasts:
__bridge_transfer:给予ARC
所有权
__bridge_retained:解除ARC
所有权
在上面代码中,"return(NSString*)CFURLCreateStringByAddingPercentEscapes",编译器弹出的修复提示有两个:
两个解决办法:__bridge和
__bridge_transfer,正确的选择应该是__bridge_transfer。
因为CFURLCreateStringByAddingPercentEscapes()函数创建了一个新 的CFStringRef对象,当然我们要的是NSString对象,因此我们使用了强制转换。实际上我们真正想要做的是:
从CFURLCreateStringByAddingPercentEscapes函数的create可以看出,函数会返回一个
retain过的对象。某个人需要负责在适当的时候释放该对象,如果我们不把这个对象返回为
NSString,则通常我们需要自己调用:
{
}
不过ARC只能作用于Objective-C对象,不能释放CoreFoundation
对象。因此这里你仍然需要调用
CFRelease()来释放该对象。
这里我们的真实意图是:转换新创建的CFStringRef对象为NSString对象,并且当我们不再需要使用这个
NSString对象时,ARC能够适当地释放它。
因此我们使用
__bridge_transfer告诉ARC:"嘿!ARC,这个CFStringRef对象现在是一个NSString对象了,我希望你来销毁它,我这里就不调用CFRelease()来释放它了"。
如果我们使用__bridge,就会导致内存泄漏。ARC并不知道自己应该在使用完对象之后释放该对象,也没有人调用CFRelease()。结果这个对象就会永远保留在内存中。因此选择正确的bridge说明符是至关重要的。
为了代码更加可读和容易理解,iOS还提供了一个辅助函数:
CFBridgingRelease()。函数所做事情和__bridge_transfer
强制转换完全一样,但更加简洁和清晰。CFBridgingRelease() 函数定义为内联函数,因此不会导致额外的开销。函数之所以命名为
CFBridgingRelease(),是因为一般你会在需要使用CFRelease()释放对象的地方,调用CFBridgingRelease()来传递对象
的所有权。
因此最后我们的代码如下:
- (NSString *)escape:(NSString *)text
{
}
另一个常见的需要CFBridgingRelease的情况是AddressBookframework:
- (NSString *)firstName
{
}
只要你调用命名为Create,Copy, Retain
的CoreFoundation
函数,你都 需要使用CFBridgingRelease()安全地将值传递给ARC。
__bridge_retained则正好相反,假设你有一个NSString对象,并且要将它传递给某个
Core FoundationAPI,该函数希望接收这个 string
对象的所有权。这时候你就不希望
ARC也去释放该对象,否则就会对同一对象释放两次,而且必将导致应用崩溃!换句话说,使用
__bridge_retained将对象的所有权给 予CoreFoundation,而 ARC
不再负责释放该对象。
如下面例子所示:
returnCFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
NULL,
(__bridgeCFStringRef)text,
NULL,
CFSTR("!*'();:@&=+$,/?%#[]"),
CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncodin
g)));
returnCFBridgingRelease(ABRecordCopyCompositeName(...));
NSString *s1 =[[NSString alloc] initWithFormat:@"Hello, %@!", name];
CFStringRef s2 =(__bridge_retained CFStringRef)s1;
// do something with s2
//. . .
CFRelease(s2);
一旦(__bridge_retainedCFStringRef)
转换完成,ARC 就不再负责释放该对象。如果你在这里使用
__bridge,应用就很可能会崩溃。ARC可能在CoreFoundation
正在使用该对象时,释放掉它。
同样__bridge_retained也有一个辅助函数:CFBridgingRetain()。从名字 就可以看出,这个函数会让
CoreFoundation
执行retain,实际如下:
现在你应该明白了,上面例子的CFRelease()是和CFBridgingRetain()对应
的。你应该很少需要使用__bridge_retained或
CFBridgingRetain()。
__bridge转换不仅仅局限于CoreFoundation
对象,某些API使用void*
指针作为参数,允许你传递任何东西的引用:Objective-C对象、Core
Foundation对象、malloc()内存缓冲区等等。void*表示这是一个指针,但实际的数据类型可以是任何东西!
要将Objective-C对象和void*互相转换,你也需要使用__bridge
转换, 如下:
在animationdelegate
方法中,你再将对象强制转回来:
{
}
总结:
使用CFBridgingRelease(),从Core Foundation
传递所有权给Objective-C;
使用CFBridgingRetain(),从Objective-C
传递所有权给CoreFoundation;
使用__brideg,表示临时使用某种类型,不改变对象的所有权。
相关文章推荐
- ARC中__bridge, __bridge__transfer, __bridge_retained 关系
- ARC中__bridge, __bridge__transfer, __bridge_retained 关系
- ARC中__bridge, __bridge__transfer, __bridge_retained 关系
- ARC中__bridge, __bridge__transfer, __bridge_retained 关系
- IOS学习笔记65 —— ARC中__bridge, __bridge__transfer, __bridge_retained 关系
- ARC中__bridge, __bridge__transfer, __bridge_retained 关系
- IOS学习笔记65 —— ARC中__bridge, __bridge__transfer, __bridge_retained 关系
- ARC中__bridge, __bridge__transfer, __bridge_retained 关系
- ARC中__bridge, __bridge__transfer, __bridge_retained 关系
- ARC中__bridge, __bridge__transfer, __bridge_retained 关系
- 浅解ARC中的 __bridge、__bridge_retained和__bridge_transfer
- iOS——ARC桥接转换中__bridge、__bridge_retained和__bridge_transfer前缀的用法
- ARC下__bridge,__bridge_retained和__bridge_transfer的意思,区别与使用
- __bridge, __bridge_transfer, __bridge_retained 在ARC环境下转换原理解析
- ARC 下__bridge、__bridge_retained、__bridge_transfer三者使用和区别
- __bridge,__bridge_transfer和__bridge_retained详解
- __bridge,__bridge_retained,__bridge_transfer
- IOS开发之__bridge,__bridge_transfer和__bridge_retained
- IOS开发之__bridge,__bridge_transfer和__bridge_retained
- 10.31 __bridge 与 __bridge_transfer 与 __bridge_retained