自动释放池

objc_autorelease内部主要函数

// - __AtAutoreleasePool 结构体
struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

struct AutoreleasePoolPageData
{
    magic_t const magic; // 16
    __unsafe_unretained id *next; // 8
    pthread_t const thread; // 8
    AutoreleasePoolPage * const parent; // 8
    AutoreleasePoolPage *child; // 8
    uint32_t const depth; // 4
    uint32_t hiwat; // 4
};

static inline void *push() {
   return autoreleaseFast(POOL_BOUNDARY);
}

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 {
1.        return autoreleaseNoPage(obj);
   }
}

//压栈操作:将对象加入AutoreleaseNoPage并移动栈顶的指针
id *add(id obj) {
    id *ret = next;
    *next = obj;
    next++;
    return ret;
}

//当前hotPage已满时调用, 新建page, 并将新建的page设置为hotpage, 然后压栈obj
static 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);
}

//当前hotpage不存在时调用, 表示第一次创建page, 就新建page并将新建的page设置为hotpage,然后压栈obj
static id *autoreleaseNoPage(id obj) {
    AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
    setHotPage(page);

    if (obj != POOL_SENTINEL) {
        page->add(POOL_SENTINEL);
    }

    return page->add(obj);
}

static inline void pop(void *token){
        AutoreleasePoolPage *page;
        id *stop;
        if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
            // 忽略无关代码
            ...
        } else {
            page = pageForPointer(token);
        }
        stop = (id *)token;
        // 忽略无关代码
        ...
        return popPage<false>(token, page, stop);
    }

// - 根据 token所在的page.
static AutoreleasePoolPage *pageForPointer(const void *p) {
        return pageForPointer((uintptr_t)p);
    }

static AutoreleasePoolPage *pageForPointer(uintptr_t p){
        AutoreleasePoolPage *result;

        #define PAGE_SIZE               I386_PGBYTES
        #define I386_PGBYTES            4096
        uintptr_t offset = p % SIZE;

        ASSERT(offset >= sizeof(AutoreleasePoolPage));

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

        return result;
    }

static void popPage(void *token, AutoreleasePoolPage *page, id *stop)
    {
        ...

        page->releaseUntil(stop);

    ...
        if (page->child) {
            // hysteresis: keep one empty child if page is more than half full
            if (page->lessThanHalfFull()) {
                page->child->kill();
            }else if (page->child->child) {
                page->child->child->kill();
            }
        }
    }

// - 给page中的对象发送release消息.
void releaseUntil(id *stop) {
        // Not recursive: we don't want to blow out the stack 
        // if a thread accumulates a stupendous amount of garbage
        
        while (this->next != stop) {
            // Restart from hotPage() every time, in case -release 
            // autoreleased more objects
            AutoreleasePoolPage *page = hotPage();

            // fixme I think this `while` can be `if`, but I can't prove it
            while (page->empty()) {
                page = page->parent;
                setHotPage(page);
            }

            page->unprotect();
            // page->next代表下一个需要插入的位置,此时是没有值的,但是上一个8字节地址是有值的,根据取值符*可以拿到对应的obj指针
            id obj = *--page->next;
            // 将page中next指向的位置改为SCRIBBLE
            memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
            page->protect();

            if (obj != POOL_BOUNDARY) {
                objc_release(obj);
            }
        }

        setHotPage(this);
        ...
    }

void kill() {
        // Not recursive: we don't want to blow out the stack 
        // if a thread accumulates a stupendous amount of garbage
        AutoreleasePoolPage *page = this;
        while (page->child) page = page->child;

        AutoreleasePoolPage *deathptr;
        do {
            deathptr = page;
            page = page->parent;
            if (page) {
                page->unprotect();
                page->child = nil;
                page->protect();
            }
            delete deathptr;
        } while (deathptr != this);
    }

// - objc_autoreleaseReturnValue  -> objc_autorelease()->objc_object::autorelease()->rootAutorelease()->objc_object::rootAutorelease2()->AutoreleasePoolPage::autorelease()
id objc_autoreleaseReturnValue(id obj){
    if (prepareOptimizedReturn(ReturnAtPlus1)) return obj;
    return objc_autorelease(obj);
}

// - 这里 autorelease 又调用到 autoreleaseFast.
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;
    }

结构

image.png

AutoreleasePoolPage 结构

  1. 首先AutoreleasePool 是一个由多个AutoreleasePoolPage组成的双向链表, 每个AutoreleasePoolPage 都有parent和child指针, 同时每个AutoreleasePoolPage又都是一个栈, 每个AutoreleasePoolPage的大小都是 4096byte(4k). 其中
    1. next id*类型,指向当前page中的下一个可插入位置.
    2. thread 当前page所在的线程
    3. parent 前一个page
    4. child 后一个page
    5. depth 深度,代表当前page在整个双向链表中的位置

objc_autoreleasePoolPush(void)

  1. 调用流程: objc_autoreleasePoolPush(void) -> AutoreleasePoolPage::push() -> autoreleaseFast(POOL_BOUNDARY);
  2. 每次push时候都会插入一个POOL_BOUNDARY(哨兵对象).
    1. hotpage存在并且没有满
      1. 保存obj变量, 然后直接移动next指针.
    2. hotpage满了
      1. 顺着page->child沿着链表查找下一个不满的page并将这个查找到的page设置为hotpage, 然后将变量保存到hotpage中.
      2. 如果查找的最后一个hotpage都是满的, 直接新建一个page,并将这个page设置为hotpage, 然后将变量保存到hotpage中.
    3. hotpage不存在
      1. 新建一个page,并将这个page设置为hotpage, 然后将变量保存到hotpage中.

objc_autoreleasePoolPop

  1. 调用流程: objc_autoreleasePoolPop(atautoreleasepoolobj) -> AutoreleasePoolPage::pop() -> pageForPointer();
  2. 函数内部实现原理:
    1. 找到hotpage,即pageN.
    2. 校验pageN是否为空,否则循环寻找pageN的父结点并赋值为hotpage
    3. 从hotpage的next减8字节,即page中最后一个autorelease对象开始,依次执行release操作,同时将page中的当前位置赋值为SCRIBBLE
      4.循环执行1-3直到当前page的next指向token(如果token不为POOL_BOUNDARY, 也执行release).
    4. 当前page设置为hotpage.

autoreleasepool 生命周期

  1. 系统创建(有些函数、方法需要返回一个对象,而系统可能在该对象被返回之前,就已经销毁了对象。那么为了保证函数、方法返回的对象在被返回之前不被销毁,我们就要使用自动释放池进行延迟销毁,如 .[NSString string]和[[NSString alloc]init]第一个因为是string函数的返回值无法在作用域结束后直接释放, 会自动加入到runloop创建的自动释放池中, 而使用alloc]init] 创建的对象, 超过作用域就直接释放, 不用加入到自动释放池中.)

    1. 即将进入Loop, 回调内部调用 _objc_autoreleasePoolPush() 创建自动释放池。
    2. BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池.
  2. 自己使用@autoreleasepool {}

    1. 会在}后, 调用 objc_autoreleasePoolPop(), 释放内部的变量.

哨兵对象

1.#define POOL_BOUNDARY nil

  1. 每当自动释放池初始化调用objc_autoreleasePoolPush方法时,总会通过AutoreleasePoolPage的push方法,将POOL_BOUNDARY放到当前page的栈顶,并且返回这个边界对象;而在自动释放池释放调用objc_autoreleasePoolPop方法时,又会将边界对象以参数传入,这样自动释放池就会向释放池中对象发送release消息,直至找到第一个边界对象为止。

返回值自动释放的原理

image.png
image.png

图中可以看到[DemoObject object]方法最终调用了objc_autoreleaseReturnValue方法, 而objc_autoreleaseReturnValue中调用到了autoreleaseFast.

测试

// DemoObject
@interface DemoObject : NSObject
@end

@implementation DemoObject

+ (DemoObject*)object {
    DemoObject *object = [[DemoObject alloc]init];
    return object;
}

@end

// AutoreleasePoolDemoVC
@interface AutoreleasePoolDemoVC ()
@end

@implementation AutoreleasePoolDemoVC

- (void)viewDidLoad {
    [super viewDidLoad];
    
    __weak id object1;
    {
        object1 = [DemoObject object];
    }
    NSLog(@"object1 = %@", object1);
    
    __weak id object2;
    {
        object2 = [[DemoObject alloc]init];
    }
    NSLog(@"object2 = %@", object2);
}

// - 第一个log打印的结果不是空的, 第二个打印的结果是空的
// - 因为第一个使用[DemoObject object] 生成的对象会被加入到runloop创建的自动释放池中, 而runloop创建的自动释放池, 需要在runloop休眠或者退出时候才会销毁.
// - 第二个是使用alloc init出来的对象, 对象在超过{}的作用域时候, 就会释放.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值