面试题引发的思考:
Q: autorelease对象在什么时机会被调用release?
- 调用时机是由 RunLoop 来控制的;
- 在某次 RunLoop循环 中,RunLoop休眠之前 调用了
release。
Q: ARC方法里有局部对象,出了方法后会立即释放吗?
- ARC生成的代码是在 方法完成之前 给 对象调用了一次
release; - 对象会在 方法结束之后 立即释放。
1. autorelease对象在什么时机会被调用release?
(0) 在MRC环境下:
// TODO: ----------------- Person类 -----------------
@interface Person : NSObject
@end
@implementation Person
- (void)dealloc {
NSLog(@"%s", __func__);
[super dealloc];
}
@end
(1) 案例一
// TODO: ----------------- ViewController类 -----------------
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"begin");
@autoreleasepool {
Person *person = [[[Person alloc] init] autorelease];
}
NSLog(@"end");
}
// 打印结果
Demo[1234:567890] begin
Demo[1234:567890] -[Person dealloc]
Demo[1234:567890] end
由打印结果可知:
在autoreleasepool中调用autorelease方法的person对象会在autoreleasepool{}结束后释放。
(2) 案例二
// TODO: ----------------- ViewController类 -----------------
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"begin");
Person *person = [[[Person alloc] init] autorelease];
NSLog(@"end");
}
// 打印结果
Demo[1234:567890] begin
Demo[1234:567890] end
Demo[1234:567890] -[Person dealloc]
由打印结果可知:
在viewDidLoad中调用autorelease方法的person对象会在viewDidLoad结束后释放。
(3) Q: 那么具体释放时机是什么呢?
1> 猜想一:main函数的autoreleasepool

程序的main函数中虽然有一个autoreleasepool,但是在程序退出之前autoreleasepool是不会结束的;
而在viewDidLoad中调用autorelease方法的person对象会在viewDidLoad结束后释放,所以肯定不是被main函数的autoreleasepool管理的
2> 猜想二:viewDidLoad方法调用完毕释放
// TODO: ----------------- ViewController类 -----------------
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[[Person alloc] init] autorelease];
NSLog(@"%s", __func__);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"%s", __func__);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"%s", __func__);
}
// 打印结果
Demo[1234:567890] -[ViewController viewDidLoad]
Demo[1234:567890] -[ViewController viewWillAppear:]
Demo[1234:567890] -[Person dealloc]
Demo[1234:567890] -[ViewController viewDidAppear:]
由打印结果可知:
在viewDidLoad中初始化的person对象会在viewDidLoad和viewWillAppear:执行完毕之后,才会调用person对象的release方法。
Q:不在viewDidLoad中初始化的autorelease对象会在什么时机会被调用release呢?
2. RunLoop和autorelease
打印主线程的RunLoop:
// TODO: ----------------- ViewController类 -----------------
- (void)viewDidLoad {
[super viewDidLoad];
// 打印主线程的RunLoop
NSLog(@"%@", [NSRunLoop mainRunLoop]);
}


- iOS在主线程的Runloop中注册了2个
Observer; - 第一个
Observer的activities = 0x1
即activities = 1表示kCFRunLoopEntry; - 第二个
Observer的activities = 0xa0
即activities =160 = 32 + 128表示kCFRunLoopBeforeWaiting | kCFRunLoopExit。
由iOS底层原理 - 探寻RunLoop本质(一)可知RunLoop内部实现逻辑:

由以上分析可知:
iOS在主线程的Runloop中注册了2个
Observer:
- 第1个
Observer:
监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush();- 第2个
Observer:
监听了kCFRunLoopBeforeWaiting事件,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush();
监听了kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()。
回顾以上的案例:
// TODO: ----------------- ViewController类 -----------------
- (void)viewDidLoad {
[super viewDidLoad];
// 这个Person什么时候调用release,是由RunLoop来控制的
// 它可能是在某次RunLoop循环中,RunLoop休眠之前调用了release
Person *person = [[[Person alloc] init] autorelease];
NSLog(@"%s", __func__);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"%s", __func__);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"%s", __func__);
}
// 打印结果
Demo[1234:567890] -[ViewController viewDidLoad]
Demo[1234:567890] -[ViewController viewWillAppear:]
Demo[1234:567890] -[Person dealloc]
Demo[1234:567890] -[ViewController viewDidAppear:]
因为person对象会在RunLoop休眠之前被释放,那么可知:
viewDidLoad和viewWillAppear处在同一次运行循环中。
3. ARC局部对象释放时机
既然autorelease对象什么时候调用release,是由RunLoop来控制的;
Q: 那ARC方法里的局部对象也是编译器自动在对象后追加autorelease,在RunLoop休眠之前调用了release吗?
ARC环境下执行以下代码:
// TODO: ----------------- ViewController类 -----------------
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
NSLog(@"%s", __func__);
// ARC环境下,相当于在方法最后release对象
//[person release];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"%s", __func__);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"%s", __func__);
}
// 打印结果
Demo[1234:567890] -[ViewController viewDidLoad]
Demo[1234:567890] -[Person dealloc]
Demo[1234:567890] -[ViewController viewWillAppear:]
Demo[1234:567890] -[ViewController viewDidAppear:]
由打印结果可知:
viewDidLoad执行完后person对象立即就被释放了,说明:
ARC生成的代码是在方法完成之前给对象调用了一次release,对象会在方法结束之后立即释放。