zoukankan      html  css  js  c++  java
  • Objective-C autoreleasepool深入理解

    Objective-C autorelease

    // main.m
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            
        }
    }
    

      

    clang -rewrite-objc main.m
    
    __AtAutoreleasePool __autoreleasepool; 
    struct __AtAutoreleasePool {
      __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
      ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
      void * atautoreleasepoolobj;
    };
    class AutoreleasePoolPage 
    {
    
    #define POOL_SENTINEL 0
        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
            4096;  // must be multiple of vm page size
    #else
            4096;  // 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(NULL), 
              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) 
        {
            (die ? _objc_fatal : _objc_inform)
                ("autorelease pool page %p corrupted
    "
                 "  magic 0x%08x 0x%08x 0x%08x 0x%08x
      pthread %p
    ", 
                 this, magic.m[0], magic.m[1], magic.m[2], magic.m[3], 
                 this->thread);
        }
    
        void check(bool die = true) 
        {
            if (!magic.check() || !pthread_equal(thread, pthread_self())) {
                busted(die);
            }
        }
    
        void fastcheck(bool die = true) 
        {
            if (! magic.fastcheck()) {
                busted(die);
            }
        }
    
    
        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();
            *next++ = obj;
            protect();
            return next-1;
        }
    
        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_SENTINEL) {
                    objc_release(obj);
                }
            }
    
            setHotPage(this);
    
    #ifndef NDEBUG
            // 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 = NULL;
                    page->protect();
                }
                delete deathptr;
            } while (deathptr != this);
        }
    
        static void tls_dealloc(void *p) 
        {
            // reinstate TLS value while we work
            setHotPage((AutoreleasePoolPage *)p);
            pop(0);
            setHotPage(NULL);
        }
    
        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 AutoreleasePoolPage *hotPage() 
        {
            AutoreleasePoolPage *result = (AutoreleasePoolPage *)
                tls_get_direct(key);
            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 {
                return autoreleaseSlow(obj);
            }
        }
    
        static __attribute__((noinline))
        id *autoreleaseSlow(id obj)
        {
            AutoreleasePoolPage *page;
            page = hotPage();
    
            // The code below assumes some cases are handled by autoreleaseFast()
            assert(!page || page->full());
    
            if (!page) {
                assert(obj != POOL_SENTINEL);
                _objc_inform("Object %p of class %s autoreleased "
                             "with no pool in place - just leaking - "
                             "break on objc_autoreleaseNoPool() to debug", 
                             obj, object_getClassName(obj));
                objc_autoreleaseNoPool(obj);
                return NULL;
            }
    
            do {
                if (page->child) page = page->child;
                else page = new AutoreleasePoolPage(page);
            } while (page->full());
    
            setHotPage(page);
            return page->add(obj);
        }
    
    public:
        static inline id autorelease(id obj)
        {
            assert(obj);
            assert(!OBJC_IS_TAGGED_PTR(obj));
            id *dest __unused = autoreleaseFast(obj);
            assert(!dest  ||  *dest == obj);
            return obj;
        }
    
    
        static inline void *push() 
        {
            if (!hotPage()) {
                setHotPage(new AutoreleasePoolPage(NULL));
            } 
            id *dest = autoreleaseFast(POOL_SENTINEL);
            assert(*dest == POOL_SENTINEL);
            return dest;
        }
    
        static inline void pop(void *token) 
        {
            AutoreleasePoolPage *page;
            id *stop;
    
            if (token) {
                page = pageForPointer(token);
                stop = (id *)token;
                assert(*stop == POOL_SENTINEL);
            } else {
                // Token 0 is top-level pool
                page = coldPage();
                assert(page);
                stop = page->begin();
            }
    
            if (PrintPoolHiwat) printHiwat();
    
            page->releaseUntil(stop);
    
            // memory: delete empty children
            // hysteresis: keep one empty child if this page is more than half full
            // special case: delete everything for pop(0)
            if (!token) {
                page->kill();
                setHotPage(NULL);
            } else if (page->child) {
                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_SENTINEL) {
                    _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);
    
            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 autoreleases 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_SENTINEL
    };
    

      

    https://opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm.auto.html

    @autoreleasepool

    双向链表

  • 相关阅读:
    【Win 10 应用开发】在后台播放视频
    【Win 10 应用开发】UI Composition 札记(八):用 XamlLight 制作灯光效果
    【Win 10 应用开发】UI Composition 札记(七):基于表达式的动画
    【Win 10 应用开发】UI Composition 札记(六):动画
    Redis中的Scan命令的使用
    MySQL8.0新特性
    IP白名单的实现(PHP)
    mysql分组后获取每个组排序后的第一条数据(整行)
    PHP 之sha256 sha512封装
    Mysql数据库中CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP区别
  • 原文地址:https://www.cnblogs.com/yangwenhuan/p/9307275.html
Copyright © 2011-2022 走看看