在MRC
环境下对象的释放需要手动调用[obj release]
方法来将对象的引用计数减1。调用release
方法后该对象的引用计数立即减1
[obj autorelease]
与release
的不同是它不会立即将对象的引用计数减1,而是延迟该对象的释放。
那么它延迟到什么时候。
autorelease
对象不会立即进行release
操作,当这个对象所在autoreleasepool
进行销毁的时候,这个对象才会进行release
操作。
那么,这个对象被添加到哪一个autoreleasepool
,这个autoreleasepool
什么时候释放。
对象创建之后默认是添加到系统维护的autoreleasepool
中。
那么系统维护的这个autoreleasepool
是什么时候释放
对于系统维护的autoreleasepool
的释放时机就要了解到线程,runloop与autoreleasepool
的关系。
每一个线程,包括主线程都会有一个NSRunLoop
对象。可以简单的把这个runloop看做是一个死循环,这个runloop不断的接收事件然后处理事件。在每一次的循环开始的时候会自动的创建一个autoreleasepool
。在循环结束的时候进行一次drain,这时autoreleasepool中的所有对象进行一次release操作。所以系统维护的autoreleasepool是在runloop的一个周期结束的时候进行释放的。
stringWithFormat
返回的是一个autorelease对象
case one
__weak id reference = nil;
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = [NSString stringWithFormat:@"a string object"];
reference = str;
NSLog(@"viewDidLoad - %@", reference);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"viewWillAppear - %@", reference);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"viewDidAppear - %@", reference);
}
输出结果
viewDidLoad - a string object
viewWillAppear - a string object
viewDidAppear - (null)
代码分析
reference
弱引用 str
,不会对str
的引用计数产生影响,用来查看str
是否已经释放。
[NSString stringWithFormat:@"a string object"]
创建一个autorelease
字符串对象,此时该对象的引用计数是1
通过str
引用该字符串对象,引用计数变为2
viewDidLoad
方法执行完毕,str
指针释放,字符串对象的引用计数变为1
autoreleasepool
drain的时候字符串对象进行release
操作,引用计数变为0,销毁该对象
由输出结果可以看到,系统维护的autoreleasepool在viewWillAppear
的时候还没有进行drain操作,在viewDidAppear
的时候已经执行了drain操作,字符串对象被释放了。
这种情况是加入到系统默认的autoreleasepool
中
case two
__weak id reference = nil;
- (void)viewDidLoad {
[super viewDidLoad];
@autoreleasepool {
NSString *str = [NSString stringWithFormat:@"a string object"];
reference = str;
}
NSLog(@"viewDidLoad - %@", reference);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"viewWillAppear - %@", reference);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"viewDidAppear - %@", reference);
}
输出结果
viewDidLoad - (null)
viewWillAppear - (null)
viewDidAppear - (null)
代码分析
viewDidLoad
中@autoreleasepool{}
即自己创建了一个自动释放池
当出了@autoreleasepool{}
作用域,该autoreleasepool
被drain,内部的字符串对象release。引用计数减一
离开了@autoreleasepool{}
作用域,局部变量str被销毁,字符串对象引用计数减一,变为0。字符串被销毁。所以reference执行变为nil
case three
__weak id reference = nil;
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = nil;
@autoreleasepool {
str = [NSString stringWithFormat:@"a string object"];
reference = str;
}
NSLog(@"viewDidLoad - %@", reference);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"viewWillAppear - %@", reference);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"viewDidAppear - %@", reference);
}
输出结果
viewDidLoad - a string object
viewWillAppear - (null)
viewDidAppear - (null)
代码分析
相比第二种情况,将str
变量放到了@autoreleasepool{}
的外边,所以在@autoreleasepool{}
之后字符串对象虽然release了一次,但是str
还在引用该对象。所以在viewDidLoad
中并没有释放。
viewDidLoad
之后,str
变量销毁,没有对字符串的引用,则字符串对象被释放。
什么时候主动使用autoreleasepool
for (int i = 0; i < 1000; i++) {
NSString *var = [NSString stringWithFormat:@""];
}
当循环中产生大量的autorelease对象,这些对象在循环结束之后并不会立即释放。在上面这个例子中,这1000个字符串对象需要到这次runloop结束时才会被释放。造成短时间内存过高。
for (int i = 0; i < 1000; i++) {
@autoreleasepool {
NSString *var = [NSString stringWithFormat:@""];
}
}
而使用了autoreleasepool之后,每次循环结束的时候autoreleasepool的作用域结束,这个对象就会立即被释放。避免造成了内存的过高。
什么样的对象默认就是autorelease的
通常非alloc
、new
、copy
、mutableCopy
出来的对象都是autorelease的,比如[UIImage imageNamed:]
、[NSString stringWithFormat]
、[NSMutableArray array]
等。
当不确定时可以通过case one 来检测是否该对象在作用域结束之后理解释放来确定。