美文网首页Runloop、释放池、事件响应
系统底层源码分析(6)——@autoreleasepool

系统底层源码分析(6)——@autoreleasepool

作者: 无悔zero | 来源:发表于2021-01-27 18:52 被阅读0次

@autoreleasepool是自动释放池,用来进行内存管理,最常应用在循环中。平常在main函数也会见到:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

@autoreleasepoolARC或者MRC下都能使用,其实@autoreleasepool就是代替了NSAutoreleasePoolNSAutoreleasePool只能在MRC下使用:

NSAutoreleasePool *pool = [NSAutoreleasePool alloc] init];//相当于objc_autoreleasePoolPush()
[pool drain];//相当于objc_autoreleasePoolPop(atautoreleasepoolobj)
  1. clang把它编译成cpp文件看一下@autoreleasepool转换成了__AtAutoreleasePool
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_g0_r4439vk96pq6tsnp9m9njklm0000gn_T_main_dc0893_mi_0);
    }
    return 0;
}
//自动释放池
struct __AtAutoreleasePool {
  __AtAutoreleasePool() {
    atautoreleasepoolobj = objc_autoreleasePoolPush();//构造函数
  }
  ~__AtAutoreleasePool() {
    objc_autoreleasePoolPop(atautoreleasepoolobj);//析构函数
  }
  void * atautoreleasepoolobj;
};
  1. objc4-750源码探索一下,先来看构造函数,使用@autoreleasepool时便会调用构造函数:
void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();//AutoreleasePool由AutoreleasePoolPage组成
}
class AutoreleasePoolPage 
{
    ...
    static inline void *push() 
    {
        id *dest;
        if (DebugPoolAllocation) {//是否没初始化过
            // Each autorelease pool starts on a new pool page.
            dest = autoreleaseNewPage(POOL_BOUNDARY);//初始化
        } else {
            dest = autoreleaseFast(POOL_BOUNDARY);//插入操作;POOL_BOUNDARY:哨兵对象,标识,边界
        }
        assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
        return dest;
    }
    ...
}
  1. 如果没有初始化过就调用autoreleaseNewPage,最终会调用page->add
class AutoreleasePoolPage 
{
    ...
    static inline void setHotPage(AutoreleasePoolPage *page) 
    {
        if (page) page->fastcheck();
        tls_set_direct(key, (void *)page);//保存
    }
    ...
    static __attribute__((noinline))
    id *autoreleaseNoPage(id obj)
    {
        ...
        AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);//初始化
        setHotPage(page);//保存到TLS

        if (pushExtraBoundary) {
            page->add(POOL_BOUNDARY);//添加标识
        }
        return page->add(obj);//添加
    }
    ...
    static __attribute__((noinline))
    id *autoreleaseNewPage(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page) return autoreleaseFullPage(obj, page);
        else return autoreleaseNoPage(obj);//还没创建,调用这个方法
    }
    ...
}
  1. 回到第3步,如果初始化过,就会调用autoreleaseFast。或者在@autoreleasepool作用域里创建对象时,会自动调用autorelease,最终也会调用autoreleaseFast
@implementation NSObject
...
- (id)autorelease {
    return ((id)self)->rootAutorelease();
}
inline id 
objc_object::rootAutorelease()
{
    if (isTaggedPointer()) return (id)this;
    if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;

    return rootAutorelease2();
}
__attribute__((noinline,used))
id 
objc_object::rootAutorelease2()
{
    assert(!isTaggedPointer());
    return AutoreleasePoolPage::autorelease((id)this);
}
class AutoreleasePoolPage 
{
    ...
    static inline id autorelease(id obj)
    {
        assert(obj);
        assert(!obj->isTaggedPointer());
        id *dest __unused = autoreleaseFast(obj);//插入操作
        assert(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
        return obj;
    }
    ...
}
class AutoreleasePoolPage 
{
    ...
    static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page && !page->full()) {//链表是否存在&链表是否满了
            return page->add(obj);
        } else if (page) {
            return autoreleaseFullPage(obj, page);//如果满了
        } else {
            return autoreleaseNoPage(obj);//没有就新建
        }
    }
    ...
}
  1. 首先会通过key取出AutoreleasePoolPage返回:
class AutoreleasePoolPage 
{
    ...
    static inline AutoreleasePoolPage *hotPage() 
    {
        AutoreleasePoolPage *result = (AutoreleasePoolPage *)
            tls_get_direct(key);//当前线程的局部私有空间取出来的,TLS,私有数据,只有当前线程能访问到
        if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
        if (result) result->fastcheck();
        return result;
    }
    ...
}
  1. 然后分情况进行add
    如果AutoreleasePoolPage存在没有满,就直接add
    如果AutoreleasePoolPage不存在,就调用autoreleaseNoPage初始化,然后再add(详细参考上面);
    如果AutoreleasePoolPage存在但满了,就调用autoreleaseFullPage再创建一个,然后再add
class AutoreleasePoolPage 
{
    ...
    static __attribute__((noinline))
    id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
    {
        do {
            if (page->child) page = page->child;
            else page = new AutoreleasePoolPage(page);//不够,再创建一个新表
        } while (page->full());

        setHotPage(page);//保存
        return page->add(obj);//添加
    }
  • AutoreleasePoolPage有大小限制,大小是4096字节,一张表不够用就会创建另外一张,以双向链表连接起来形成一个自动释放池:
内存结构
  1. 接着就来看看add操作,一开始添加的是传入的POOL_BOUNDARY,之后的才是添加需要自动释放的对象。POOL_BOUNDARY哨兵对象的作用是作为标识,用来区别每个 AutoreleasePoolPage边界:
class AutoreleasePoolPage 
{
    ...
    id *add(id obj)
    {
        assert(!full());
        unprotect();
        id *ret = next;  // faster than `return next-1` because of aliasing
        *next++ = obj;//添加后开始地址自然要变
        protect();
        return ret;
    }
    ...
}
  1. 然后来看析构函数,出释放池作用域时会调用:
void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}
class AutoreleasePoolPage 
{
    ...
    static inline void pop(void *token) 
    {
        ...
        page = pageForPointer(token);//取出
        ...
        page->releaseUntil(stop);//链表当中的对象进行release操作
        ...
    }
    ...
}
  1. 先取出AutoreleasePoolPage
class AutoreleasePoolPage 
{
    ...
    static AutoreleasePoolPage *pageForPointer(uintptr_t p) 
    {
        AutoreleasePoolPage *result;
        uintptr_t offset = p % SIZE;//偏移量

        assert(offset >= sizeof(AutoreleasePoolPage));

        result = (AutoreleasePoolPage *)(p - offset);
        result->fastcheck();

        return result;
    }
    ...
}
  1. 然后进行释放:
class AutoreleasePoolPage 
{
    ...
    void releaseUntil(id *stop) 
    {
        while (this->next != stop) {//POOL_BOUNDARY
            // Restart from hotPage() every time, in case -release 
            // autoreleased more objects
            AutoreleasePoolPage *page = hotPage();

            while (page->empty()) {
                page = page->parent;
                setHotPage(page);
            }

            page->unprotect();
            id obj = *--page->next;//找到插入的对象
            memset((void*)page->next, SCRIBBLE, sizeof(*page->next));//置空
            page->protect();

            if (obj != POOL_BOUNDARY) {
                objc_release(obj);//释放
            }
        }
        ...
    }
    ...
}
void 
objc_release(id obj)
{
    if (!obj) return;
    if (obj->isTaggedPointer()) return;
    return obj->release();
}

每调用一次push就会创建autoreleasepool,然后往 AutoreleasePoolPage中的next位置插入一个POOL_BOUNDARY,并返回插入的 POOL_BOUNDARY内存地址。之后便是把需要释放的对象添加到next指向的位置。当pop时,把POOL_BOUNDARY之后的所有autoreleased对象释放。

  • 线程、Runloop、释放池三者关系
  1. 每个线程都有对应的Runloop,并且会在有需要的时候自动创建。

  2. 每一个自动释放池对应一个线程,但一个线程可能有多个自动释放池。自动释放池会保存到线程的局部储存空间中,每个线程都会维护自己的自动释放池。线程结束时,会进行释放。

随之释放
  1. 在当前线程的每个event loop开始前,系统会自动创建一个autoreleasepool,并在event loop结束时释放。

比如常驻线程下,使用定时器:

- (void)timer {
    [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(test) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] run];
}
- (void)test {
    //__autoreleasing,在ARC中会自动注册到autoreleasepool中
    __autoreleasing id test = [NSObject new];
}

或者说,线程的Runloop注册了两个Observer
Runloop状态为kCFRunLoopEntry时,autoreleasepool会进行push操作;
Runloop状态为kCFRunLoopBeforeWaiting时,autoreleasepool会先进行pop操作,再进行push操作;
Runloop状态为kCFRunLoopExit时,autoreleasepool会进行pop操作。
(状态变化具体可看Runloop执行流程

  • 多层嵌套@autoreleasepool
@autoreleasepool {
    @autoreleasepool {
        @autoreleasepool {
        
        }
    }
}

要注意的是,多层嵌套并不是创建多个AutoreleasePoolPage,而是使用同一个,只是每有一个@autoreleasepool都会插入一个POOL_BOUNDARY哨兵对象,当一个AutoreleasePoolPage的大小不够用时,才会创建新的AutoreleasePoolPage

嵌套

相关文章

网友评论

    本文标题:系统底层源码分析(6)——@autoreleasepool

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