iOS中内存管理问题之: Core Foundation 和 C
Cocoa Foundation是iOS中的一个重要框架, 我们大量的使用了里面封装好的接口. 但是还有许多我们需要的接口并不包含在Cocoa框架中, 比如RSA算法, MD5算法, SHA1算法, AES加密算法等, Cocoa对象库里并没有相应的实现. 这时候我们可以在Core Foundation框架里面去寻找. 包括NSString类里没有的字符串编码GBK, GB2312, GB18030等, 在Core Foundation里, 都能找到相应的编码. 建立Socket连接, 获得输入流和输出流时, 也需要使用Core Foundation里的CFNetwork Api等等.
Cocoa Foundation和Core Foundation的一个重要区别是: Cocoa Foundation是面向对象的, Core Foundation是非面向对象的. 这是造成内存管理方式区别的原因. Cocoa Foundation中的对象使用ARC自动管理内存, 而对于Core Foundation中的对象则需要进行手动管理内存, 使用完之后都需要手动Release.
然后又涉及到另一个问题, 就是Cocoa Foundation和Core Foundation相互转换时内存管理问题. Cocoa Foundation和Core Foundation对象相互转换时我们使用__bridge, __bridge_transfer, __bridge_retained
关键字.
- 1.
__bridge
: Cocoa Foundation和Core Foundation对象转化时只涉及对象类型不涉及对象所有权的转化; - 2.
__bridge_transfer
: Cocoa Foundation对象转换成Core Foundation对象时,将Cocoa Foundation对象的所有权交给Core Foundation对象, 此时ARC就能自动管理该内存; - 3.
__bridge_retained
: (与__bridge_transfer
相反)常用在将Cocoa Foundation对象转换成Core Foundation对象时, 将Cocoa Foundation对象的所有权交给Core Foundation对象来管理;
当使用_bridge_retained
标识符以后, 代表Cocoa Foundation要将对象所有权交给Core Foundation对象自己来管理, 所以我们要在各种Ref使用完成以后用Release将其手动释放.
举个栗子, FMDB中的一个大家都知道的一个方法:
- (void)inDatabase:(void (^)(FMDatabase *db))block {
/* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue
* and then check it against self to make sure we're not about to deadlock. */
FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);
assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock");
FMDBRetain(self);
dispatch_sync(_queue, ^() {
FMDatabase *db = [self database];
block(db);
if ([db hasOpenResultSets]) {
NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");
#if defined(DEBUG) && DEBUG
NSSet *openSetCopy = FMDBReturnAutoreleased([[db valueForKey:@"_openResultSets"] copy]);
for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) {
FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue];
NSLog(@"query: '%@'", [rs query]);
}
#endif
}
});
FMDBRelease(self);
}
MDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);
这句代码中使用了__bridge
关键字. MDatabaseQueue
继承自NSObject, NSObject是Cocoa的根类. 而dispatch_get_specific(const void *key);
则返回"the current subsystem-specific context"
, 因而__bridge
在代码中的作用便是把Core Foundation对象转换成Cocoa Foundation对象, 但是由于只涉及对象类型的转换而没有涉及对象控制权的转换, 所有还需要手动的进行Retain和Release. 在代码中体现为:
FMDBRetain(self);
FMDBRelease(self);
文章有参考网络文章,如有错误,欢迎讨论指出。