自动引用计数
1.1 什么是自动引用计数
自动引用计数是指内存管理中对引用采取自动计数的技术
1.2 内存管理/引用计数
生成并持有对象:alloc/new/copy/mutableCopy
持有对象:retain
释放对象:release
废弃对象:dealloc
自己生成的对象,自己所持有:
id obj = [[NSObject alloc] init]; // 指向 生成并持有对象的指针 被赋值给变量obj
非自己生成的对象,自己也能持有:
id obj = [NSMutableArray array]; // 取得的对象存在,但自己不持有对象 NSMutableArray类对象被赋值给obj,但变量obj自己并不持有该对象
[obj retain]; // 使用retain方法可以持有对象
不在需要自己持有的对象时释放:
id obj = [[NSObject alloc] init]; // 自己生成并持有对象
[obj release]; // 释放对象
用某个方法生成对象,并将其返回给该方法的调用方:
- (id) allocObject {
id obj = [[NSObject alloc] init];
return obj;
}
原封不动地返回用alloc方法生成并持有的对象,就能让调用方也持有该对象。
调用[NSMutableArray array]使取得的对象存在,但自己不持有对象:
- (id)object {
id obj = [[NSObject alloc] init];
[obj autorelease];
return obj;
}
使用了autorelease方法,可以使取得的对象存在,但自己不持有对象。
release:立即释放
autorelease:不立即释放,注册到autoreleasepool中,pool结束时自动调用release方法
无法释放非自己持有的对象:
id obj = [NSObject object]; // 取得的对象存在,但自己不持有对象
[obj release]; // 释放非自己持有的对象,应用程序崩溃
苹果采用散列表(引用计数表)来管理引用计数
引用计数表各记录中存有内存块地址,可从各个记录追溯到各对象的内存块
1.3 ARC规则
id obj = [[NSObject alloc] init]; // id 和 对象类型在没有明确指定所有权修饰符时,默认为__strong修饰符 等同于 id __strong obj = [[NSObject alloc] init];
ARC:
{
id __strong obj = [[NSObject alloc] init];
}
// 此源代码明确指定了C语言的变量的作用域
MRC:
{
id obj = [[NSObject alloc] init];
[obj release];
}
// 为了释放生成并持有的对象,增加了 release 方法;该源代码进行的动作同ARC的动作完全一样
附有__strong修饰符的变量obj在超出其变量作用域时,即在该变量被废弃时,会释放其被赋予的对象
{
// 自己生成并持有对象
id __strong obj = [[NSObject alloc] init];
}
// 因为变量obj超出其作用域,强引用失效,所以自动地释放自己持有的对象
通过__strong 修饰符,不必再次键入retain或者release,完美地满足了“引用计数内存管理的思考方式”:
自己生成的对象,自己所持有
非自己生成的对象,自己也能持有
不再需要自己持有的对象时释放
非自己持有的对象无法释放
@interface Test : NSObject
{
id __strong obj_;
}
- (void)setObject:(id __strong)obj;
@end
@implementation Test
- (id)init
{
self = [super init];
return self;
}
- (void)setObject:(id __strong)obj
{
obj_ = obj;
}
@end
循环引用:
{
id test0 = [[Test alloc] init]; // 对象A test0持有Test对象A的强引用
id test1 = [[Test alloc] init]; // 对象B test1持有Test对象B的强引用
[test0 setObject:test1]; // 对象A的obj_成员变量持有对象B的强引用
[test1 setObject:test0]; // 对象B的obj_成员变量持有对象A的强引用
}
// test0变量超出其作用域,强引用失效,所以自动释放对象A
// test1变量超出其作用域,强引用失效,所以自动释放对象B
// 此时,持有对象A的强引用的变量为 对象B的obj_ ; 持有对象B的强引用的变量为 对象A的obj_
// 发生内存泄漏
// 循环引用很容易发生内存泄漏 <内存泄漏就是应当废弃的对象在超出其生存周期后继续存在>
__weak 修饰符提供弱引用,弱引用不能持有对象实例
id __weak obj = [[NSObject alloc] init]; // 编译器对此会给出警告⚠️
// 此源代码将自己生成并持有的对象赋值给附有__weak修饰符的变量obj。即变量obj持有对持有对象的弱引用。因此,为了不以自己持有的状态来保存自己生成并持有的对象,生成的对象会立即被释放
id __unsafe_unretained obj = [[NSObject alloc] init]; // ⚠️
__unsafe_unretained修饰符同__weak一样,因为自己生成并持有的对象不能继续为自己所有,所以生成的对象会立即被释放
__unsafe_unretained修饰的变量既不持有对象的强引用也不持有对象的弱引用
@autoreleasepool {
id __autoreleasing obj = [[NSObject alloc] init];
}