iOS底层原理 - 内存管理 之 RunLoop和autore
面试题引发的思考:
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函数
程序的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]);
}
observers
Run Loop Observer Activities
- iOS在主线程的Runloop中注册了2个
Observer
; - 第一个
Observer
的activities = 0x1
即activities = 1
表示kCFRunLoopEntry
; - 第二个
Observer
的activities = 0xa0
即activities =160 = 32 + 128
表示kCFRunLoopBeforeWaiting | kCFRunLoopExit
。
由iOS底层原理 - 探寻RunLoop本质(一)可知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
,对象会在方法结束之后立即释放。