• AutoReleasePool 工作机制


    AutoReleasePool 工作机制

    背景:

    相比很多人都有这个困惑,@autoreleasepool 到底做了什么,今天我们就从源码的基础上去分析,彻底揭开它神秘面纱

    源码路径:

    https://opensource.apple.com/tarballs/objc4/

    得益于objc 开源,我们可以一窥其本质,在接下来分析中我会尽可能引入源码

    @autoreleasepool 转成c++到底是什么样的

    Objective-C
      @autoreleasepool
        {

            NSObject *pObjecdt = nil;
            pObjecdt = [[Test alloc] init];
            [pObjecdt autorelease];
            NSLog(@"refcount = %d", CFGetRetainCount(pObjecdt));
        }

    OC C++

     使用命令

    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

    就可以把对对应m 转成c++

    Objective-C
     {
             __AtAutoreleasePool __autoreleasepool;

            NSObject *pObjecdt = __null;
            pObjecdt = ((Test *(*)(id, SEL))(void *)objc_msgSend)((id)((Test *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Test"), sel_registerName("alloc")), sel_registerName("init"));
            ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)pObjecdt, sel_registerName("autorelease"));
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_x4_q4k0pbd17n90b9x6_v_h_rb40000gn_T_main_092efe_mi_0, CFGetRetainCount(pObjecdt));
        }

    也就是说@autoreleasepool 转换成了__AtAutoreleasePool __autoreleasepool;

    下边就介绍__AtAutoreleasePool的定义

    __AtAutoreleasePool

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

    通过上边可以看的出来 这是很简单的结构图,遵循的RAII的原则,也就是说构造的时候生成atautoreleasepoolobj 指针,而 西沟的时候把atautoreleasepoolobj 传入到objc_autoreleasePoolPop中去,所以要搞清楚AutoReleasePool 就要搞清楚objc_autoreleasePoolPush 与 objc_autoreleasePoolPop 是干啥的。

    objc_autoreleasePoolPush

    C++
    struct AutoreleasePoolPageData
    {
            magic_t const magic;
            __unsafe_unretained id *next;
            pthread_t const thread;
            AutoreleasePoolPage * const parent;
            AutoreleasePoolPage *child;
            uint32_t const depth;
            uint32_t hiwat;
    };

    class AutoreleasePoolPage : private AutoreleasePoolPageData
    {
    }

    void *
    objc_autoreleasePoolPush(void)
    {
        return AutoreleasePoolPage::push();
    }
    #   define POOL_BOUNDARY nil

        static inline void *push()
        {
            id *dest;
            dest = autoreleaseFast(POOL_BOUNDARY);
            return dest;
        }
            //obj = 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 {
                    return autoreleaseNoPage(obj);
                }
            }
               
                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;
                }
                class AutoreleasePoolPage
                {
                    //obj = POOL_BOUNDARY
                    id *add(id obj)
                    {
               
                        id *ret;
               
                 
                        ret = next;  // faster than `return next-1` because of aliasing
                        *next++ = obj;
               
                        return ret;
                    }
                }
           

    通过上边调用关系可以看的出来,AutoreleasePoolPage 是跟线程相关的,push首先从tls 中取出当前线程相关连的AutoreleasePoolPage指针,类似于opengl context的原理,把POOL_BOUNDARY 放入到AutoreleasePoolPage的next位置的地方


    objc_autoreleasePoolPop

    C++
    void
    objc_autoreleasePoolPop(void *ctxt)
    {
        AutoreleasePoolPage::pop(ctxt);
    }
        void AutoreleasePoolPage::pop(void *token)
        {
            AutoreleasePoolPage *page;
            id *stop;
          
            return popPage<false>(token, page, stop);
        }
       
         template<bool allowDebug>
        static void
        popPage(void *token, AutoreleasePoolPage *page, id *stop)
        {
            page->releaseUntil(stop);
         }

        //stop 最接近的一个POOL_BOUNDARY 的地址
        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();

                id obj = *--page->next;

                if (obj != POOL_BOUNDARY) {
                    objc_release(obj);
                }
            }
        }
        void
        objc_release(id obj)
        {
            if (obj->isTaggedPointerOrNil()) return;
            return obj->release();
        }
       
                inline void
            objc_object::release()
            {
                ASSERT(!isTaggedPointer());
           
                rootRelease(true, RRVariant::FastOrMsgSend);
            }
           
           
            ALWAYS_INLINE bool
            objc_object::rootRelease()
            {
                return rootRelease(true, RRVariant::Fast);
            }
            bool
            objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant)
            {
                

                // don't check newisa.fast_rr; we already called any RR overrides
                uintptr_t carry;
                newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--
                //#define slowpath(x) (__builtin_expect(bool(x), 0))
                //    __builtin_expect((x),1)表示 x 的值为真的可能性更大;
                //bool isDeallocating() {
                //  return extra_rc == 0;
       
                if (slowpath(newisa.isDeallocating()))
                    goto deallocate;
                 return false;
             deallocate:
               if (performDealloc) {
                    ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
                }
             }

    Autorelease 源码解析

    [pObjecdt autorelease];

    也就是看下这个里边到底在干什么

    C++
    // Replaced by ObjectAlloc
    - (id)autorelease {
        return _objc_rootAutorelease(self);
    }
        id _objc_rootAutorelease(id obj)
        {
            ASSERT(obj);
            return obj->rootAutorelease();
        }
            inline id objc_object::rootAutorelease()
            {
                if (isTaggedPointer()) return (id)this;
               
                return rootAutorelease2();
            }
                id objc_object::rootAutorelease2()
                {
                    ASSERT(!isTaggedPointer());
                    return AutoreleasePoolPage::autorelease((id)this);
                }
               
                    static inline id AutoreleasePoolPage::autorelease(id obj)
                    {
                        id *dest __unused = autoreleaseFast(obj);
                        return obj;
                    }
                        static inline id *AutoreleasePoolPage::autoreleaseFast(id obj)
                        {
                          //这里就跟PoolPush 方法调用逻辑是一样的了,就把这个指针入到page页面里面
                        }

    经过上边分析,我们看下边代码情景会生发生什么

    Case 1:

    Objective-C
        NSObject *pObjecdt = nil;
        @autoreleasepool
        {
            pObjecdt = [[Test alloc] init];
            [pObjecdt autorelease];
            NSLog(@"refcount = %d", CFGetRetainCount(pObjecdt));
        }
        NSLog(@"refcount = %d", CFGetRetainCount(pObjecdt));
       

    Case 2:

    Objective-C
        @autoreleasepool {
            NSObject *pObjecdt1 = [[[Test alloc] init] retain];
            @autoreleasepool
            {

                NSObject *pObjecdt = [[Test alloc] init];
                [pObjecdt autorelease];
                _objc_autoreleasePoolPrint();
            }
        }

    相关源码

    Objective-C
    #ifndef Test_h
    #define Test_h

    @interface Test : NSObject
    {
       
    }
    -(void)dealloc;
    + (NSString *)description;
    @end
    #endif /* Test_h */


     

    Objective-C

    #import
    #import "Test.h"
    @implementation Test
    -(void)dealloc
    {
        NSLog(@"Test Destory!");
    }
    + (NSString *)description
    {
        return @"Test";
    }
    @end

    总结:

    也就是auto release pool 只有调用对象调用 auto release才会起作用,如果没有,可以忽略这个auto release pool

  • 相关阅读:
    Spring Boot 2020 官方基础68课程第24个 gateway
    手把手带你体验一场属于Linux的学习之旅
    贪心算法——活动安排问题
    运维系列.在Docker中使用Grafana
    js处理字符串转数组,数组转字符串
    油溶性CdSeTe/ZnS量子点,发光波长640nm-820nm
    机器人中的数值优化(十一)——高斯牛顿法、LMF方法、Dogleg方法
    List基本使用(C++)
    软件项目移交运维计划
    中秋月饼还没有恰到,先用css画一个月亮赏赏眼
  • 原文地址:https://blog.csdn.net/c553110519/article/details/126353861