美文网首页
iOS底层原理 - 内存管理 之 RunLoop和autore

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

作者: hazydream | 来源:发表于2019-11-22 10:26 被阅读0次

    面试题引发的思考:

    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对象会在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中注册了2个Observer
    • 第一个Observeractivities = 0x1
      activities = 1表示kCFRunLoopEntry
    • 第二个Observeractivities = 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休眠之前被释放,那么可知:

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

    相关文章

      网友评论

          本文标题:iOS底层原理 - 内存管理 之 RunLoop和autore

          本文链接:https://www.haomeiwen.com/subject/gfyfwctx.html