iOS内存管理
iOS内存管理
引用计数(Reference Count)
iOS的内存管理基于引用计数这一机制,简单地说就是一个对象被其他对象持有时,引用计数+1,当不再被持有时-1,当引用计数减为零的时候对象不被任何人持有,就会被释放。
内存管理的思考方式
- 自己生成的对象自己持有
id obj = [[NSObject alloc] init];
- 不是自己生成的对象自己也能持有
id obj = [NSMutableArray array];
[obj retain];
- 不再需要自己所持有的对象时释放
id obj = [[NSObject alloc] init];
[obj release]
- 不是自己持有的对象不能释放
id obj = [NSMutableArray array];
[obj release]
程序崩溃(obj
并不持有NSMutableArray
对象)
对象操作与对应的Objective-C方法
对象操作 | Objective-C方法 | 对应的操作结果 |
---|---|---|
生成并持有对象 |
Alloc ,new ,copy ,mutableCopy 等方法 |
生成对象并设置引用计数=1 |
持有对象 | retain |
引用计数+1 |
释放对象 | release |
引用计数-1 |
释放对象 |
dealloc (系统自动调用) |
引用计数=0时调用 |
使用某个方法生成对象,其源码实现:
-(id)allocObject
{
id obj = [[NSObject alloc] init]; //生成并持有了NSObject对象
return obj;
}
这里原封不动地将obj
返回给方法的调用者,使得方法的调用者也持有该对象。
我们考虑另一种情况:
-(void)object
{
id obj = [[NSObject alloc] init]; //生成并持有了NSObject对象
[obj autorelease]; //对象存在但不再持有
return obj
}
这里使用autorelease
方法将obj
对象注册到autoreleasepool
中,在对象超出指定的生存范围的时候能够自动正确地释放(调用release
方法)
autorelease
autorelease
类似于C语言的自动变量的特性,当自动变量超出其作用域时,就会被自动释放。但是autorelease
与C语言不通的是,你可以自己定义自动变量的作用域。
autorelease
的使用方法:
① 生成并持有NSAutoreleasePool
对象
② 调用已分配的对象的autorelease
实例方法
③ 释放NSAutoreleasePool
对象
当我们在调用autorelease
方法时,其内部其实是调用了[NSAutoreleasePool addObject:self]
方法,从而将对象放入自动释放池中,当自动释放池被释放时,自动释放池里的对象也被一并释放了。
ARC
__strong修饰符
__strong修饰符是id类型和对象类型的默认修饰符id obj = [[NSObject alloc] init]
与id __strong obj [[NSObject alloc] init]
等价
__strong表示对对象的强引用,当变量超出其作用区域的时候,强引用会失效,此时对象不再被持有,就会被释放。
{
id __strong obj = [[NSObject alloc] init];
//此时obj对NSObject对象强引用
}
//超出作用区域,强引用失效,NSObject对象不再被持有,于是被释放
使用__strong修饰的变量可以互相赋值
id __strong obj0 = [[NSObject alloc] init]; //对象A
id __strong obj1 = [[NSObject alloc] init]; //对象B
id __strong obj2 = nil
obj0 = obj1
/* obj0被赋值,所以其对对象A的强引用失效,对象A不再被
* 持有,于是被释放
* obj1对对象B有一个强引用,现在obj1赋值给obj0,于是
* obj0也有对象B的强引用,此时对象B的强引用对象为
* obj1、obj0
*/
obj2 = obj0
//同样的此时的obj0对对象B有强引用,当obj0赋值给obj2
//之后obj2也有对对象B的强引用,此时对象B的强引用变量
//为obj0、obj1、obj2
obj1 = nil
//因为obj1被置为nil,所以obj1对对象B的强引用失效
//此时对象B的强引用对象为obj0、obj2
obj0 = nil
//因为obj0被置为nil,所以obj0对对象B的强引用失效
//此时对象B的强引用对象为obj2
obj2 = nil
//因为obj0被置为nil,所以obj0对对象B的强引用失效
//此时对象B不再被任何人持有,于是被释放
循环引用
下面我们用代码来重现循环引用,声明一个Test类
@interface Test:NSObject
{
id __strong obj_;
}
-(void)setObject:(id __strong)obj;
@end
@implemnetaion Test
-(id)init
{
self = [super init];
return self
}
-(void)setObject:(id __strong)obj
{
obj_=obj;
}
@end
Test类有一个成员变量obj_
,并且实现了obj_
的setter
方法setObject:
下面为循环引用发生的代码
{
id test0 = [[Test alloc] init]; //Test对象A
//test0对对象A有强引用
id test1 = [[Test alloc] init]; //Test对象B
//test1对对象B有强引用
[test0 setObject:test1];
//对象A的成员变量obj_对对象B有强引用
//此时对象B的强引用变量有test1、对象A的成员变量obj_
[test1 setObject:test0]
//对象B的成员变量obj_对对象A有强引用
//此时对象A的强引用变量有test0、对象B的成员变量obj_
}
/* 出了作用域
* test0对对象A的强引用失效,释放对象A,但是此时对象B的成员
* 变量obj_还持有对对象A的强引用,导致对象A无法释放
* test1对对象B的强引用失效,释放对象B,但是此时对象A的成员
* 变量obj_还持有对对象B的强引用,导致对象B无法释放
* 于是内存泄漏
*/
当只有一个对象时,也可以发生循环引用,例如
id test = [[Test alloc] init];
[test setObject:test];
此时
__weak修饰符
weak