iOS底层原理 - 内存管理 之 RunLoop和autore

2019-11-22  本文已影响0人  hazydream

面试题引发的思考:

Q: autorelease对象在什么时机会被调用release

Q: ARC方法里有局部对象,出了方法后会立即释放吗?


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对象会在viewDidLoadviewWillAppear:执行完毕之后,才会调用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本质(一)可知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休眠之前被释放,那么可知:

viewDidLoadviewWillAppear处在同一次运行循环中。


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,对象会在方法结束之后立即释放。

上一篇下一篇

猜你喜欢

热点阅读