美文网首页
内存管理(三)

内存管理(三)

作者: DoBetter1 | 来源:发表于2020-02-20 16:25 被阅读0次

    Strong:苹果默认修饰符。
    objc_storeStrong存储strong源码:

    void
    objc_storeStrong(id *location, id obj)
    {
        id prev = *location;
        if (obj == prev) {
            return;
        }
        objc_retain(obj);
        *location = obj;
        objc_release(prev);
    }
    

    分析: retain 新值, release 旧值(感觉有点简单)

    Strong这里就是这么简单吗?答案肯定不是,下面查看objc_storeStrong方法在哪里使用的。

    可以看到在setIvar的时候,使用了objc_storeStrong方法。

    static ALWAYS_INLINE 
    void _object_setIvar(id obj, Ivar ivar, id value, bool assumeStrong)
    {
        if (!obj  ||  !ivar  ||  obj->isTaggedPointer()) return;
    
        ptrdiff_t offset;
        objc_ivar_memory_management_t memoryManagement;
        _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);
    
        if (memoryManagement == objc_ivar_memoryUnknown) {
            if (assumeStrong) memoryManagement = objc_ivar_memoryStrong;
            else memoryManagement = objc_ivar_memoryUnretained;
        }
    
        id *location = (id *)((char *)obj + offset);
    
        switch (memoryManagement) {
        case objc_ivar_memoryWeak:       objc_storeWeak(location, value); break;
        case objc_ivar_memoryStrong:     objc_storeStrong(location, value); break;
        case objc_ivar_memoryUnretained: *location = value; break;
        case objc_ivar_memoryUnknown:    _objc_fatal("impossible");
        }
    }
    

    分析:如果objc_ivar_memoryUnknown也会设置objc_ivar_memoryStrong,这里就会调用上面的objc_storeStrong方法,这里的objc_ivar_memoryUnretained需要注意的是这个直接是一个值传递,不是指针传递,容易发生内存泄露。

    自动释放池:
    @autoreleasepool {
    }
    

    选中main.m文件,Show in Finder

    进入当前文件夹路径

    输入clang -rewrite-objc main.m -o main.cpp

    可以看到多了一个main.cpp,打开看最后面,main函数被编译成下面这段代码:

    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_9y_ksm15f2145nds9lxg10f8xk40000gn_T_main_e3776d_mi_0);
        }
        return 0;
    }
    

    分析:/* @autoreleasepool */ 被编译成 __AtAutoreleasePool __autoreleasepool;
    在main.cpp文件中查找__AtAutoreleasePool:

    struct __AtAutoreleasePool {
      __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
      ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
      void * atautoreleasepoolobj;
    };
    

    发现是一个结构体其实__AtAutoreleasePool()是构造方法,~__AtAutoreleasePool()析构方法,这里插一个知识点:首先修改main.m文件夹扩展名为main.mm开启混编模式,添加如下代码:

    #import <Foundation/Foundation.h>
    
    struct FXTest {
     
        FXTest(){
            printf("1111 - %s", __func__);
        };
        
        ~FXTest(){
            printf("2222 - %s", __func__);
        };
    };
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            FXTest test;
            NSLog(@"Hello, World!");
        }
        return 0;
    }
    

    打印如下:

    有了这个铺垫,就可以确认__AtAutoreleasePool的构造方法和析构方法调用顺序。

    先调用objc_autoreleasePoolPush();会返回一个atautoreleasepoolobj对象,然后调用objc_autoreleasePoolPop(atautoreleasepoolobj);(注意atautoreleasepoolobj其实就是边界符,后面会讲)

    源码查找objc_autoreleasePoolPush()方法:

    void *
    objc_autoreleasePoolPush(void)
    {
        return AutoreleasePoolPage::push();
    }
    

    分析:::代表属性函数,意思就是调用AutoreleasePoolPage的push方法。

    进入AutoreleasePoolPage类,源码如下:

    class AutoreleasePoolPage 
    {
        // EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is 
        // pushed and it has never contained any objects. This saves memory 
        // when the top level (i.e. libdispatch) pushes and pops pools but 
        // never uses them.
    #   define EMPTY_POOL_PLACEHOLDER ((id*)1)
    
    #   define POOL_BOUNDARY nil
        static pthread_key_t const key = AUTORELEASE_POOL_KEY;
        static uint8_t const SCRIBBLE = 0xA3;  // 0xA3A3A3A3 after releasing
        static size_t const SIZE = 
    #if PROTECT_AUTORELEASEPOOL
            PAGE_MAX_SIZE;  // must be multiple of vm page size
    #else
            PAGE_MAX_SIZE;  // size and alignment, power of 2
    #endif
        static size_t const COUNT = SIZE / sizeof(id);
    
        magic_t const magic;
        id *next;
        pthread_t const thread;
        AutoreleasePoolPage * const parent;
        AutoreleasePoolPage *child;
        uint32_t const depth;
        uint32_t hiwat;
    
        // SIZE-sizeof(*this) bytes of contents follow
    
        static void * operator new(size_t size) {
            return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
        }
        static void operator delete(void * p) {
            return free(p);
        }
    
        inline void protect() {
    #if PROTECT_AUTORELEASEPOOL
            mprotect(this, SIZE, PROT_READ);
            check();
    #endif
        }
    
        inline void unprotect() {
    #if PROTECT_AUTORELEASEPOOL
            check();
            mprotect(this, SIZE, PROT_READ | PROT_WRITE);
    #endif
        }
    
        AutoreleasePoolPage(AutoreleasePoolPage *newParent) 
            : magic(), next(begin()), thread(pthread_self()),
              parent(newParent), child(nil), 
              depth(parent ? 1+parent->depth : 0), 
              hiwat(parent ? parent->hiwat : 0)
        { 
            if (parent) {
                parent->check();
                assert(!parent->child);
                parent->unprotect();
                parent->child = this;
                parent->protect();
            }
            protect();
        }
    
        ~AutoreleasePoolPage() 
        {
            check();
            unprotect();
            assert(empty());
    
            // Not recursive: we don't want to blow out the stack 
            // if a thread accumulates a stupendous amount of garbage
            assert(!child);
        }
    
    
        void busted(bool die = true) 
        {
            magic_t right;
            (die ? _objc_fatal : _objc_inform)
                ("autorelease pool page %p corrupted\n"
                 "  magic     0x%08x 0x%08x 0x%08x 0x%08x\n"
                 "  should be 0x%08x 0x%08x 0x%08x 0x%08x\n"
                 "  pthread   %p\n"
                 "  should be %p\n", 
                 this, 
                 magic.m[0], magic.m[1], magic.m[2], magic.m[3], 
                 right.m[0], right.m[1], right.m[2], right.m[3], 
                 this->thread, pthread_self());
        }
    
        void check(bool die = true) 
        {
            if (!magic.check() || !pthread_equal(thread, pthread_self())) {
                busted(die);
            }
        }
    
        void fastcheck(bool die = true) 
        {
    #if CHECK_AUTORELEASEPOOL
            check(die);
    #else
            if (! magic.fastcheck()) {
                busted(die);
            }
    #endif
        }
    
    
        id * begin() {
            return (id *) ((uint8_t *)this+sizeof(*this));
        }
    
        id * end() {
            return (id *) ((uint8_t *)this+SIZE);
        }
    
        bool empty() {
            return next == begin();
        }
    
        bool full() { 
            return next == end();
        }
    
        bool lessThanHalfFull() {
            return (next - begin() < (end() - begin()) / 2);
        }
    
        id *add(id obj)
        {
            assert(!full());
            unprotect();
            id *ret = next;  // faster than `return next-1` because of aliasing
            *next++ = obj;
            protect();
            return ret;
        }
    
        void releaseAll() 
        {
            releaseUntil(begin());
        }
    
        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();
                id obj = *--page->next;
                memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
                page->protect();
    
                if (obj != POOL_BOUNDARY) {
                    objc_release(obj);
                }
            }
    
            setHotPage(this);
    
    #if DEBUG
            // we expect any children to be completely empty
            for (AutoreleasePoolPage *page = child; page; page = page->child) {
                assert(page->empty());
            }
    #endif
        }
    
        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);
        }
    
        static void tls_dealloc(void *p) 
        {
            if (p == (void*)EMPTY_POOL_PLACEHOLDER) {
                // No objects or pool pages to clean up here.
                return;
            }
    
            // reinstate TLS value while we work
            setHotPage((AutoreleasePoolPage *)p);
    
            if (AutoreleasePoolPage *page = coldPage()) {
                if (!page->empty()) pop(page->begin());  // pop all of the pools
                if (DebugMissingPools || DebugPoolAllocation) {
                    // pop() killed the pages already
                } else {
                    page->kill();  // free all of the pages
                }
            }
            
            // clear TLS value so TLS destruction doesn't loop
            setHotPage(nil);
        }
    
        static AutoreleasePoolPage *pageForPointer(const void *p) 
        {
            return pageForPointer((uintptr_t)p);
        }
    
        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;
        }
    
    
        static inline bool haveEmptyPoolPlaceholder()
        {
            id *tls = (id *)tls_get_direct(key);
            return (tls == EMPTY_POOL_PLACEHOLDER);
        }
    
        static inline id* setEmptyPoolPlaceholder()
        {
            assert(tls_get_direct(key) == nil);
            tls_set_direct(key, (void *)EMPTY_POOL_PLACEHOLDER);
            return EMPTY_POOL_PLACEHOLDER;
        }
    
        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;
        }
    
        static inline void setHotPage(AutoreleasePoolPage *page) 
        {
            if (page) page->fastcheck();
            tls_set_direct(key, (void *)page);
        }
    
        static inline AutoreleasePoolPage *coldPage() 
        {
            AutoreleasePoolPage *result = hotPage();
            if (result) {
                while (result->parent) {
                    result = result->parent;
                    result->fastcheck();
                }
            }
            return result;
        }
    
    
        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);
            }
        }
    
        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", 
                             pthread_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);
        }
    
    
        static __attribute__((noinline))
        id *autoreleaseNewPage(id obj)
        {
            AutoreleasePoolPage *page = hotPage();
            if (page) return autoreleaseFullPage(obj, page);
            else return autoreleaseNoPage(obj);
        }
    
    public:
        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;
        }
    
    
        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);
            }
            assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
            return dest;
        }
    
        static void badPop(void *token)
        {
            // Error. For bincompat purposes this is not 
            // fatal in executables built with old SDKs.
    
            if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) {
                // OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
                _objc_fatal
                    ("Invalid or prematurely-freed autorelease pool %p.", token);
            }
    
            // Old SDK. Bad pop is warned once.
            static bool complained = false;
            if (!complained) {
                complained = true;
                _objc_inform_now_and_on_crash
                    ("Invalid or prematurely-freed autorelease pool %p. "
                     "Set a breakpoint on objc_autoreleasePoolInvalid to debug. "
                     "Proceeding anyway because the app is old "
                     "(SDK version " SDK_FORMAT "). Memory errors are likely.",
                         token, FORMAT_SDK(sdkVersion()));
            }
            objc_autoreleasePoolInvalid(token);
        }
        
        static inline void pop(void *token) 
        {
            AutoreleasePoolPage *page;
            id *stop;
    
            if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
                // Popping the top-level placeholder pool.
                if (hotPage()) {
                    // Pool was used. Pop its contents normally.
                    // Pool pages remain allocated for re-use as usual.
                    pop(coldPage()->begin());
                } else {
                    // Pool was never used. Clear the placeholder.
                    setHotPage(nil);
                }
                return;
            }
    
            page = pageForPointer(token);
            stop = (id *)token;
            if (*stop != POOL_BOUNDARY) {
                if (stop == page->begin()  &&  !page->parent) {
                    // Start of coldest page may correctly not be POOL_BOUNDARY:
                    // 1. top-level pool is popped, leaving the cold page in place
                    // 2. an object is autoreleased with no pool
                } else {
                    // Error. For bincompat purposes this is not 
                    // fatal in executables built with old SDKs.
                    return badPop(token);
                }
            }
    
            if (PrintPoolHiwat) printHiwat();
    
            page->releaseUntil(stop);
    
            // memory: delete empty children
            if (DebugPoolAllocation  &&  page->empty()) {
                // special case: delete everything during page-per-pool debugging
                AutoreleasePoolPage *parent = page->parent;
                page->kill();
                setHotPage(parent);
            } else if (DebugMissingPools  &&  page->empty()  &&  !page->parent) {
                // special case: delete everything for pop(top) 
                // when debugging missing autorelease pools
                page->kill();
                setHotPage(nil);
            } 
            else 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();
                }
            }
        }
    
        static void init()
        {
            int r __unused = pthread_key_init_np(AutoreleasePoolPage::key, 
                                                 AutoreleasePoolPage::tls_dealloc);
            assert(r == 0);
        }
    
        void print() 
        {
            _objc_inform("[%p]  ................  PAGE %s %s %s", this, 
                         full() ? "(full)" : "", 
                         this == hotPage() ? "(hot)" : "", 
                         this == coldPage() ? "(cold)" : "");
            check(false);
            for (id *p = begin(); p < next; p++) {
                if (*p == POOL_BOUNDARY) {
                    _objc_inform("[%p]  ################  POOL %p", p, p);
                } else {
                    _objc_inform("[%p]  %#16lx  %s", 
                                 p, (unsigned long)*p, object_getClassName(*p));
                }
            }
        }
    
        static void printAll()
        {        
            _objc_inform("##############");
            _objc_inform("AUTORELEASE POOLS for thread %p", pthread_self());
    
            AutoreleasePoolPage *page;
            ptrdiff_t objects = 0;
            for (page = coldPage(); page; page = page->child) {
                objects += page->next - page->begin();
            }
            _objc_inform("%llu releases pending.", (unsigned long long)objects);
    
            if (haveEmptyPoolPlaceholder()) {
                _objc_inform("[%p]  ................  PAGE (placeholder)", 
                             EMPTY_POOL_PLACEHOLDER);
                _objc_inform("[%p]  ################  POOL (placeholder)", 
                             EMPTY_POOL_PLACEHOLDER);
            }
            else {
                for (page = coldPage(); page; page = page->child) {
                    page->print();
                }
            }
    
            _objc_inform("##############");
        }
    
        static void printHiwat()
        {
            // Check and propagate high water mark
            // Ignore high water marks under 256 to suppress noise.
            AutoreleasePoolPage *p = hotPage();
            uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
            if (mark > p->hiwat  &&  mark > 256) {
                for( ; p; p = p->parent) {
                    p->unprotect();
                    p->hiwat = mark;
                    p->protect();
                }
                
                _objc_inform("POOL HIGHWATER: new high water mark of %u "
                             "pending releases for thread %p:", 
                             mark, pthread_self());
                
                void *stack[128];
                int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
                char **sym = backtrace_symbols(stack, count);
                for (int i = 0; i < count; i++) {
                    _objc_inform("POOL HIGHWATER:     %s", sym[i]);
                }
                free(sym);
            }
        }
    
    #undef POOL_BOUNDARY
    };
    
    // anonymous namespace
    };
    

    分析:代码有点长基本都会说到,先混个脸熟,重点
    magic_t const magic; //里面有4个32位,4*4个字节也就是16字节,用来校验 AutoreleasePoolPage的结构是否完整。
    id *next; //8个字节,指向最新添加的autoreleased对象的下一个位置,初始化时指向begin()。
    pthread_t const thread; //8个字节,指向当前线程。
    AutoreleasePoolPage * const parent; //8个字节,指向父节点,第一个结点的parent值为nil。
    AutoreleasePoolPage *child; //8个字节,指向子节点,最后一个结点的child值为nil。
    uint32_t const depth;//4个字节,代表深度,从0开始,往后递增1。
    uint32_t hiwat; //4个字节,代表high water mark。
    这里加起来一共56个字节,也就是说AutoreleasePoolPage的成员变量已总共56个字节。

    自动释放池是一个双向链表,这里的parent和child可以证明,一个指向父节点,一个指向子节点。

    自动释放池的作用是容纳变量,在合适的时机释放,在哪些情况需要使用自动释放池:

    1. 大量的临时变量。
    2. 非UI操作,命令行。
    3. 自己创建辅助线程。、
      PAGE_MAX_SIZE = 4096 (一页所能容纳的大小)

    那么自动释放池出去自己占用的内存,可以容纳多少个变量呢?
    (4096 - 56个自己占用的) / 8(每个变量8个字节) = 505(也就是说可以容纳505个变量)。那么超出505个会怎么样呢?用代码测试下:

    extern void _objc_autoreleasePoolPrint(void);
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            for (int i= 0; i<505; i++) {
                NSObject *objc = [[[NSObject alloc] init] autorelease];
            }
            _objc_autoreleasePoolPrint();
        }
        return 0;
    }
    

    分析:autorelease为添加到自动释放池(mrc必须要添加自动释放池,arc会自动加),_objc_autoreleasePoolPrint函数为扩展自动释放池状态打印,打印如下:


    分析:可以看到505个对象地址是从0x104002000开始的,到0x104002038一共 3*16+8=56个字节,就是自动释放池自己占用的内存,然后在从0x104002040开始存储第一个对象,中间隔了8个字节(注意是16进制,8+8=10),注意这8个字节就是边界符([0x104002038] ################ POOL 0x104002038),作用就是做标记,在移除的时候做边界判断。

    总结:自动释放池总容积是4096,属性站了56个字节,我们会不断的往自动释放池push压栈对象,在压入之前会先设置一个边界符,加到容积满了之后,会开辟新的页面(page),并且会有新的边界符,然后继续压栈,后续会在pop出栈。

    下面进行AutoreleasePoolPage源码分析:

    void *
    objc_autoreleasePoolPush(void)
    {
        return AutoreleasePoolPage::push();
    }
    

    进入push:

     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);
            }
            assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
            return dest;
        }
    

    分析:首先DebugPoolAllocation判断是否需要开辟新的页面,如果需要调用autoreleaseNewPage创建,如果不需要utoreleaseFast直接压入。

    首先查看autoreleaseNewPage源码:

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

    分析:取出hotPage,如果存在,返回autoreleaseFullPage(obj,page)。

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

    分析:重点do-while循环,目的循环查找page->child知道找到子页面不满的,如果找不到,就创建新的AutoreleasePoolPage,然后设置hot页,最后添加obj后返回。

    id *add(id obj)
        {
            assert(!full());
            unprotect();//其实就是解锁
            id *ret = next;  // faster than `return next-1` because of aliasing
            *next++ = obj; //指针地址移动
            protect();//其实就是加锁
            return ret;
        }
    

    接下来看下objc_autoreleasePoolPop源码:(这里的ctxt就是前面传过来了边界符,后面变为token)

    void
    objc_autoreleasePoolPop(void *ctxt)
    {
        AutoreleasePoolPage::pop(ctxt);
    }
    

    进入pop:

    static inline void pop(void *token) 
        {
            AutoreleasePoolPage *page;
            id *stop;
    
            if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
                // Popping the top-level placeholder pool.
                if (hotPage()) {
                    // Pool was used. Pop its contents normally.
                    // Pool pages remain allocated for re-use as usual.
                    pop(coldPage()->begin());
                } else {
                    // Pool was never used. Clear the placeholder.
                    setHotPage(nil);
                }
                return;
            }
    
            page = pageForPointer(token);
            stop = (id *)token;
            if (*stop != POOL_BOUNDARY) {
                if (stop == page->begin()  &&  !page->parent) {
                    // Start of coldest page may correctly not be POOL_BOUNDARY:
                    // 1. top-level pool is popped, leaving the cold page in place
                    // 2. an object is autoreleased with no pool
                } else {
                    // Error. For bincompat purposes this is not 
                    // fatal in executables built with old SDKs.
                    return badPop(token);
                }
            }
    
            if (PrintPoolHiwat) printHiwat();
    
            page->releaseUntil(stop);
    
            // memory: delete empty children
            if (DebugPoolAllocation  &&  page->empty()) {
                // special case: delete everything during page-per-pool debugging
                AutoreleasePoolPage *parent = page->parent; 
                page->kill();
                setHotPage(parent);
            } else if (DebugMissingPools  &&  page->empty()  &&  !page->parent) {
                // special case: delete everything for pop(top) 
                // when debugging missing autorelease pools
                page->kill();
                setHotPage(nil);
            } 
            else 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();
                }
            }
        }
    

    分析:首先判断是否是hotpage,如果是的话 pop(coldPage()->begin());
    重点取出当前页的parent,kill当前页,然后把parent设置为hotPage,然后一层一层递归销毁,知道回到最开始,下面看下具体是如何kill的。

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

    分析:首先判断while循环找page->child,就是说要先kill子页面,然后让当前page=page->parent(父页面),然后page->child=nil,因为也是while循环直到是自己,也就是说把自己之后的子页面都置为nil。

    总结:
    push: 自动释放池在push对象的时候,如果有页面就直接加入,如果页面满了就创建新页面,通过next++ = objc
    pop:
    找到自己page,然后找parent->page,最后kill(page),吧parent设置为hotpage,--page->next和++类似,然后objc_release(obj)释放对象(这也是出了作用域空间,变量自动释放的原因)
    作用:延迟释放,一旦超出作用域空间,就会release自动释放

    相关文章

      网友评论

          本文标题:内存管理(三)

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