AutoreleasePool

作者: 周二可 | 来源:发表于2018-04-08 11:31 被阅读44次

    首先我们明确什么对象会自动加入 autoreleasepool :

    • MRC 下需要对象调用 autorelease 才会入池, ARC 下可以通过 __autoreleasing 修饰符,否则的话看方法名,通过调用 alloc/new/copy/mutablecopy 以外的方法取得的对象,编译器帮我们自动加入 autoreleasepool (使用 alloc/new/copy/mutablecopy 方法进行初始化时,由系统管理对象,在适当的位置 release,不加入 autoreleasepool )。
    • 使用 array 会自动将返回对象注册到 autoreleasepool。
    • __weak 修饰的对象,为了保证在引用时不被废弃,会注册到 autoreleasepool 中。
    • id 的指针或对象的指针,在没有显式指定时会被注册到 autoreleasepool 中。

    实现

    autoreleasepool 是没有单独的内存结构的,它是通过以 AutoreleasePoolPage 为结点的双向链表来实现的
    通过阅读源码可知

    • 每一个线程的 autoreleasepool 其实就是一个指针的堆栈;
    • 每一个指针代表一个需要 release 的对象或者 POOL_SENTINEL(哨兵对象,代表一个 autoreleasepool 的边界);
    • 一个 pool token 就是这个 pool 所对应的 POOL_SENTINEL 的内存地址。当这个 pool 被 pop 的时候,所有内存地址在 pool token 之后的对象都会被 release ;
    • 这个堆栈被划分成了一个以 page 为结点的双向链表。pages 会在必要的时候动态地增加或删除;
    • Thread-local storage(线程局部存储)指向 hot page ,即最新添加的 autoreleased 对象所在的那个 page 。

    下面是其内存结构

    class AutoreleasePoolPage {
        magic_t const magic;
        id *next;
        pthread_t const thread;
        AutoreleasePoolPage * const parent;
        AutoreleasePoolPage *child;
        uint32_t const depth;
        uint32_t hiwat;
    };
    

    1.magic 用来校验 AutoreleasePoolPage 的结构是否完整;
    2.next 指向最新添加的 autoreleased 对象的下一个位置,初始化时指向 begin() ;
    3.thread 指向当前线程;
    4.parent 指向父结点,第一个结点的 parent 值为 nil ;
    5.child 指向子结点,最后一个结点的 child 值为 nil ;
    6.depth 代表深度,从 0 开始,往后递增 1;
    7.hiwat 代表 high water mark 。
    另外,当 next == begin() 时,表示 AutoreleasePoolPage 为空;当 next == end() 时,表示 AutoreleasePoolPage 已满。

    runLoop与AutoreleasePool

    根据苹果官方文档中对 NSRunLoop 的描述,我们可以知道每一个线程,包括主线程,都会拥有一个专属的 NSRunLoop 对象,并且会在有需要的时候自动创建。

    Each NSThread object, including the application’s main thread, has an NSRunLoop object automatically created for it as needed.

    同样的,根据苹果官方文档中对 NSAutoreleasePool 的描述,我们可知,在主线程的 NSRunLoop 对象(在系统级别的其他线程中应该也是如此,比如通过 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 获取到的线程)的每个 event loop 开始前,系统会自动创建一个 autoreleasepool ,并在 event loop 结束时 drain 。

    The Application Kit creates an autorelease pool on the main thread at the beginning of every cycle of the event loop, and drains it at the end, thereby releasing any autoreleased objects generated while processing an event.

    另外,NSAutoreleasePool 中还提到,每一个线程都会维护自己的 autoreleasepool 堆栈。换句话说 autoreleasepool 是与线程紧密相关的,每一个 autoreleasepool 只对应一个线程。

    Each thread (including the main thread) maintains its own stack of NSAutoreleasePool objects.

    弄清楚 NSThread、NSRunLoop 和 NSAutoreleasePool 三者之间的关系可以帮助我们从整体上了解 Objective-C 的内存管理机制,清楚系统在背后到底为我们做了些什么,理解整个运行机制等。

    runLoop与AutoreleasePool

    从上图可以看出从程序启动到加载完成是一个完整的运行循环,然后会停下来,等待用户交互,用户的每一次交互都会启动一次运行循环,来处理用户所有的点击事件、触摸事件。然后当系统检测到触摸事件后会开启新的 event loop ,在开始前,系统会自动创建一个 autoreleasepool ,并在 event loop 结束时 drain。

    1.http://blog.sunnyxx.com/2014/10/15/behind-autorelease/
    2.https://github.com/Draveness/analyze/blob/master/contents/objc/%E8%87%AA%E5%8A%A8%E9%87%8A%E6%94%BE%E6%B1%A0%E7%9A%84%E5%89%8D%E4%B8%96%E4%BB%8A%E7%94%9F.md
    3.http://blog.leichunfeng.com/blog/2015/05/31/objective-c-autorelease-pool-implementation-principle/

    相关文章

      网友评论

        本文标题:AutoreleasePool

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