iOS - autoreleasepool 源码

简介: AutoreleasePool:自动释放池是 Objective-C 开发中的一种自动内存回收管理的机制,为了替代开发人员手动管理内存,实质上是使用编译器在适当的位置插入release、autorelease等内存释放操作。当对象调用 autorelease 方法后会被放到自动释放池中延迟释放时机,当缓存池需要清除dealloc时,会向这些 Autoreleased 对象做 release 释放操作。

AutoreleasePool:自动释放池是 Objective-C 开发中的一种自动内存回收管理的机制,为了替代开发人员手动管理内存,实质上是使用编译器在适当的位置插入release、autorelease等内存释放操作。当对象调用 autorelease 方法后会被放到自动释放池中延迟释放时机,当缓存池需要清除dealloc时,会向这些 Autoreleased 对象做 release 释放操作。


结论:


push时


(1). 当前page存在且没有满时,直接将对象添加到当前page中,即next指向的位置;


(2). 当前page存在并且已满时,创建一个新的page,并将对象添加到新创建的page 中,然后将这两个链表节点进行链接。


(3). 当前page不存在时,创建第一个page ,并将对象添加到新创建的page中。


pop时


这里的stop同样是POOL_BOUNDARY的地址


(1). 外部循环挨个遍历autoreleased 对象,直到遍历到哨兵对象POOL_BOUNDARY。


(2). 如果当前page没有 POOL_BOUNDARY,并且为空,则将hotPage设置为当前page的父节点。


(3). 给当前autoreleased对象发送release消息。


(4). 最后再次配置hotPage。


autoreleasepool与子线程


在NSThread退出了之后,与NSThread对应的AutoReleasePool也会被自动清空,所以当一个线程结束的时候,就会回收♻️AutoReleasePool中自动释放的对象。


每一个线程都会维护自己的AutoReleasePool,而每一个AutoReleasePool都会对应唯一一个线程,但是线程可以对应多个AutoReleasePool。


autoreleasepool与NSRunloop


子线程在使用autorelease对象的时候,会懒加载出来一个AutoreleasePoolPage,然后将对象插入进去。


那么问题又来了,autorelease对象在什么时候释放的呢?也就说AutoreleasePoolPage在什么时候调用了pop方法?


其实在上面创建一个NSThread的时候,在调用[NSthread exit]的时候,会释放当前资源,也就是把当前线程关联的autoReleasePool释放掉,而在这里当RunLoop执行完成退出的时候,也会执行pop方法,这就说明了为什么在子线程当中,我们没有显示的调用pop,它也能释放当前AutoreleasePool的资源的原因。


autoreleasepool与主线程


0x1代表的是kCFRunLoopEntry,也就是说第一个 Observer监视的事件是Entry(即将进入Loop时),其回调内会调用_objc_autoreleasePoolPush()创建一个自动释放池。其order优先级是-2147483647,优先级最高,保证创建自动释放池发生在其他所有回调之前。

0xa0对应的是kCFRunLoopBeforeWaiting和kCFRunLoopExit,也就是说第二个Observer监视了两个事件:kCFRunLoopBeforeWaiting准备进入休眠,kCFRunLoopExit即将退出RunLoop。在kCFRunLoopBeforeWaiting事件时调用 _objc_autoreleasePoolPop()和_objc_autoreleasePoolPush() 释放旧的自动释放池并创建新的自动释放池;在kCFRunLoopExit事件时调用_objc_autoreleasePoolPop() 来释放自动释放池,同时这个Observer的order优先级是 2147483647,优先级最低,保证其释放自动释放池的操作发生在其他所有回调之后。


所以在没有手动增加AutoreleasePool的情况下,Autorelease对象都是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池push和pop操作。


总结:


对于不同线程,应当创建自己的AutoReleasePool。如果应用长期存在,应该定期drain和创建新的AutoReleasePool,AutoReleasePool与RunLoop 与线程是一一对应的关系,AutoReleasePool在RunLoop在开始迭代时做push操作,在RunLoop休眠或者迭代结束时做pop操作。


源码


/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_44_1ht3l6g55dv59_5s62wsv_bm0000gn_T_ViewController_3cfd72_mi_0);
    }

extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);
struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};


每个AutoreleasePoolPage大小是4kb集4096字节

class AutoreleasePoolPage;
struct AutoreleasePoolPageData
{
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
    struct AutoreleasePoolEntry {
        uintptr_t ptr: 48;
        uintptr_t count: 16;
        static const uintptr_t maxCount = 65535; // 2^16 - 1
    };
    static_assert((AutoreleasePoolEntry){ .ptr = MACH_VM_MAX_ADDRESS }.ptr == MACH_VM_MAX_ADDRESS, "MACH_VM_MAX_ADDRESS doesn't fit into AutoreleasePoolEntry::ptr!");
#endif
    magic_t const magic;
    __unsafe_unretained id *next;
    pthread_t const thread;
    AutoreleasePoolPage * const parent;
    AutoreleasePoolPage *child;
    uint32_t const depth;
    uint32_t hiwat;
    AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
        : magic(), next(_next), thread(_thread),
          parent(_parent), child(nil),
          depth(_depth), hiwat(_hiwat)
    {
    }
};

static inline void *push() 
    {
        id *dest;
        if (slowpath(DebugPoolAllocation)) {
            // Each autorelease pool starts on a new pool page.
            dest = autoreleaseNewPage(POOL_BOUNDARY);
        } else {
            dest = autoreleaseFast(POOL_BOUNDARY);
        }
        ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
        return dest;
    }


关于DebugPoolAllocation的定义

//翻译:当自动释放池无序弹出时停止,并允许堆调试器跟踪自动释放池
OPTION( DebugPoolAllocation,      OBJC_DEBUG_POOL_ALLOCATION,      "halt when autorelease pools are popped out of order, and allow heap debuggers to track autorelease pools")

id *autoreleaseNewPage(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page) return autoreleaseFullPage(obj, page);
        else return autoreleaseNoPage(obj);
    }

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);
        }
    }


hotPage

static inline AutoreleasePoolPage *hotPage() 
    {
        AutoreleasePoolPage *result = (AutoreleasePoolPage *)
            tls_get_direct(key);
        if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
        if (result) result->fastcheck();
        return result;
    }


autoreleaseFullPage

static __attribute__((noinline))
    id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
    {
        // The hot page is full. 
        // Step to the next non-full page, adding a new page if necessary.
        // Then add the object to that page.
        ASSERT(page == hotPage());
        ASSERT(page->full()  ||  DebugPoolAllocation);
        do {
            if (page->child) page = page->child;
            else page = new AutoreleasePoolPage(page);
        } while (page->full());
        setHotPage(page);
        return page->add(obj);
    }

static __attribute__((noinline))
    id *autoreleaseNoPage(id obj)
    {
        // "No page" could mean no pool has been pushed
        // or an empty placeholder pool has been pushed and has no contents yet
        ASSERT(!hotPage());
        bool pushExtraBoundary = false;
        if (haveEmptyPoolPlaceholder()) {
            // We are pushing a second pool over the empty placeholder pool
            // or pushing the first object into the empty placeholder pool.
            // Before doing that, push a pool boundary on behalf of the pool 
            // that is currently represented by the empty placeholder.
            pushExtraBoundary = true;
        }
        else if (obj != POOL_BOUNDARY  &&  DebugMissingPools) {
            // We are pushing an object with no pool in place, 
            // and no-pool debugging was requested by environment.
            _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
                         "autoreleased with no pool in place - "
                         "just leaking - break on "
                         "objc_autoreleaseNoPool() to debug", 
                         objc_thread_self(), (void*)obj, object_getClassName(obj));
            objc_autoreleaseNoPool(obj);
            return nil;
        }
        else if (obj == POOL_BOUNDARY  &&  !DebugPoolAllocation) {
            // We are pushing a pool with no pool in place,
            // and alloc-per-pool debugging was not requested.
            // Install and return the empty pool placeholder.
            return setEmptyPoolPlaceholder();
        }
        // We are pushing an object or a non-placeholder'd pool.
        // Install the first page.
        AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
        setHotPage(page);
        // Push a boundary on behalf of the previously-placeholder'd pool.
        if (pushExtraBoundary) {
            page->add(POOL_BOUNDARY);
        }
        // Push the requested object or pool.
        return page->add(obj);
    }


参考:


apple源码

iOS -- Autorelease & AutoreleasePool



相关文章
|
7月前
|
移动开发 前端开发 数据安全/隐私保护
【工具】iOS代码混淆工具-iOS源码混淆
【工具】iOS代码混淆工具-iOS源码混淆
78 1
|
7月前
|
移动开发 前端开发 安全
最强大的 iOS 应用源码保护工具:Ipa Guard,保护你的商业机密代码
最强大的 iOS 应用源码保护工具:Ipa Guard,保护你的商业机密代码
|
监控 Android开发 iOS开发
盘点一对一直播源码iOS系统维持平台稳定功能(一):弹性扩缩容
参考代码:弹性扩缩容如何实现?System.out.println("扩容:增加直播平台实例"); currentCapacity++; } private void scaleDown() { System.out.println("缩容:减少直播平台实例");
盘点一对一直播源码iOS系统维持平台稳定功能(一):弹性扩缩容
|
移动开发 安全 前端开发
最强大的iOS应用源码保护工具:Ipa Guard,保护你的商业机密代码
iOS加固保护是直接针对ios ipa二进制文件的保护技术,可以对iOS APP中的可执行文件进行深度混淆、加密。使用任何工具都无法逆向、破解还原源文件。对APP进行完整性保护,防止应用程序中的代码及资源文件被恶意篡改。Ipa Guard通过修改 ipa 文件中的 macho 文件中二进制数据(代码模块配置)进行操作,无需源码。不限定开发技术平台。支持oc,swift,cocos2d-x、unity3d、quick-cocos,html5 ,react native等等各种开发技术。Ipa Guard主要包含代码混淆全面、资源文件处理、不需要源代码更安全、调试信息清理、即时测试运行。
|
6月前
|
Linux 数据库 iOS开发
超级签名源码/超级签/ios分发/签名端本地linux服务器完成签名
该系统完全在linux下运行,不存在使用第三方收费工具,市面上很多系统都是使用的是第三方收费系统,例如:某心签名工具,某测侠等,不开源而且需要每年交费,这种系统只是在这些工具的基础上套了一层壳。请需要系统的放大你们的眼睛。
51 0
|
存储 视频直播 iOS开发
山东布谷科技iOS端实现直播app源码秒开技术(二):缓冲功能
缓冲功能对直播app源码平台用户有着提升观看短视频、直播体验的作用;对直播app源码平台有着提升直播质量,增加观众黏性的好处。因此,直播平台应当重视并充分发挥缓冲功能的潜力,为用户继续打造更好的直播观看体验。
山东布谷科技iOS端实现直播app源码秒开技术(二):缓冲功能
|
监控 安全 Android开发
直播软件APP源码iOS提交到APP store系列之上架指南
以上说明就是我为大家整理的直播软件APP平台iOS上架的相关知识,希望对大家有帮助。但是,上架只是一个开始,开发者还需要不断改进和优化应用,提供更好的用户体验和服务
直播软件APP源码iOS提交到APP store系列之上架指南
|
API 开发工具 iOS开发
一点就通,社交源码IOS客户端开发集成SDK
所谓SDK,全称是SoftwaredevelopmentKit,翻译成软件开发工具包。SDK用助开发某种软件,今天给大家简单讲解下如何在社交源码IOS客户端上开发集成 SDK。
|
API 开发工具 iOS开发
一点就通,社交源码IOS客户端开发集成SDK
所谓SDK,全称是SoftwaredevelopmentKit,翻译成软件开发工具包。SDK用助开发某种软件,今天给大家简单讲解下如何在社交源码IOS客户端上开发集成 SDK。
|
监控 Android开发 iOS开发
Android6.0 源码修改之 仿IOS添加全屏可拖拽浮窗返回按钮
Android6.0 源码修改之 仿IOS添加全屏可拖拽浮窗返回按钮
150 0