ARC 概述
有关Objective-C内存管理的方法,实际上不包括在该语言中,而是包含在Cocoa框架中用于OS X,iOS应用开发。Cocoa框架中Foundation框架类库的NSObject类担负内存管理的职责。
NSZone 是防止内存碎片化而引入的结构。对内存分配的区域本身进行多重化管理,根据使用对象的目的、对象的大小分配内存,从而提高了内存管理的效率。但是正如苹果官方文档Programing With ARC Release Notes
中所说,现在的运行时系统只是简单地忽略了区域的概念。运行时系统中的内存管理本身已极具效率,使用区域来管理内存反而会引起内存使用效率低下记忆源代码复杂化等问题。
GNUStep将引用计数保存在对象占用头文件内部的变量中,而苹果则是保存在引用技术表的记录中。
通过内存块头部管理引用计数的好处:
- 少量代码块就可完成
- 能够统一管理引用计数用内存块和对象用内存块
通过引用计数表来管理引用计数的好处:
- 对象用内存块的分配无需考虑内存块头部;
- 引用计数表各记录中存在内存块地址,可从各个记录追溯到各对象的内存块;
- 在利用工具检测内存泄露时,引用计数表的各记录也有助于检测各对象的持有者是否存在。
NSAutoReleasePool
NSRunloop每次循环过程中NSAutoReleasePool对象被生成或遗弃。
通常在使用Objective-C中,也就是Foundation框架中,无论调用哪一个对象的autorelease实例方法,实际上调用的都是NSObject类的autorelease实例方法。但是对于NSAutoreleasePool类,autorelease实例方法已被该类重载,因此运行时会出错。
GNUstep中的autorelease实际上用一种特殊的方法来实现的。这种方法能够高效地运行OS X,iOS用应用程序中频繁地调用autorelease方法,它被称为”IMP Caching
“。在运行方法调用时,为了解决类名/方法名已经取得方法名时的函数指针,要在框架初始化时对其结果进行缓存。其运行效率一般而言也是其他方法的2倍。
id autorelease_class = [NSAutoreleasePool class];
SEL autorelease_sel = @selector(addObject:);
IMP autorelease_imp = [autorelease_class methodForSelector:autorelease_sel];
- (id) autorelease
{
(*autorelease_imp)(autorelease_class,autorelease_sel,self);
}
NSAutoReleasePool调试用非公开方法
[NSAutoreleasePool showPools];
extern void _objc_autoreleasePoolPrint();
_objc_autoreleasePoolPrint()
所有权修饰符
__strong,__weak,__autoreleasing修饰符保证将有这些修饰符的自动变量初始化为nil。
NSMachPort类不支持__weak修饰符,这些类重写了retain/release并实现该类独有的引用计数机制。但是赋值以及使用附有__weak修饰符的变量都必须恰当地使用objc4
运行时库中的函数。因此独自实现引用计数机制的类大多不支持__weak修饰符。不支持__weak修饰符的类,其类声明中附加了"__attribute__((objc_arc_weak_reference_unvailable
)"这一属性,同时定义了NS_AUTOMATED_WEAK_UNAVAILABLE
。
/// __strong是默认的所有权修饰符
id __strong obj = [[NSObject alloc] init];
/// __weak 在持有某对象的若引用时,若该对象被废弃,则此弱引用将自动失效且处于nil被赋值的状态(空弱引用)
id __weak obj1 = obj;
/// 编译器会检查方法名是否以alloc/new/copy/mutablecopy开始,如果不是则自动将返回值的对象注册到autoreleasepool中。在访问附有__weak修饰符的变量时必须访问注册到autoreleasepool的对象,这是因为__weak修饰符只持有对象的弱引用,而在访问引用对象的过程中,该对象有可能被废弃。如果要把访问的对象注册到autoreleasepool中,那么在@autoreleasepool块结束之前都能确保该对象存在。因此在使用附有__weak修饰符的变量时,最好先暂时赋值给附有__strong修饰符的变量后再使用。
id __autoreleasing tmp = obj1;
/// id的指针或者对象的指针在没有显示指定时会被附上__autoreleasing修饰符。比如经常会在方法的参数中传递NSError对象的指针。
NSError *error = nil;
// NSError **pError = &error; /// 编译错误
/// 所有权修饰符必须一致
NSError * __strong *pError = &error;
/// 对象指针类赋值时,起所有权修饰符必须保持一致,但是下列源代码没有警告。因为编译器自动地将源代码转化。
NSError __strong *error1 = nil;
BOOL result1 = [self performOperationWithError:&error1];
/// 转化后形式如下:
NSError __strong *error2 = nil;
NSError __autoreleasing *tmp2 = error2;
BOOL result2 = [self performOperationWithError:&error2];
error2 = tmp2;
__bridge
C语言的结构体(struct和union)中,不能存在Objective-C对象型变量。因为C语言的规约上没有方法来管理结构体成员的生存周期。要把对象性变量加入到结构体成员中,可强制转换void 或是附件__unsafe_unretained修饰符。附有__unsafe_unretained*修饰符的变量不属于编译器的内存管理对象。
struct Data
{
NSMutableArray __unsafe_unretained *array;
};
/// id型或对象性变量赋值给void*或者逆向赋值时都需要进行特定的转换。
id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;
/// __bridge_retained 转换可使要转换赋值的变量也持有所赋值的对象 __bridge_transfer 被转换的变量所持有的对象在该变量被赋值给转换目标变量后随之释放。__bridge不改变对象的持有情况,只是简单赋值。
CFMutableArrayRef cfObject = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
/// 因为赋值给附有__strong修饰符的变量中,因此发生了强引用。超出作用域后obj1强引用失效,但是cfObject没有手动释放,内存泄露
id obj1 = (__bridge id)cfObject;
/// 因为赋值给附有__strong修饰符的变量中,因此发生了强引用。超出作用域后obj1强引用失效,但是cfObject通过__bridge_transfer已经释放,没有内存泄露。
id obj2 = (__bridge_transfer id)cfObject;
属性
- assign --> __unsafe_unretained修饰符
- copy --> __strong修饰符(但是赋值的是被复制的对象)
- retain --> __strong修饰符
- strong --> __strong修饰符
- unsafe_unretained --> __unsafe_unretained修饰符
- weak --> __weak修饰符
网友评论