A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

引言:这篇文章旨在从runtime源码中分析出 引用计数 值本身的保存位置,适合对底层原理有兴趣的朋友,或者面试造火箭的同学(比如百度的面试官非常喜欢问底层原理:好,我知道你说了深浅复制的区别一大堆,如果我让你自己实现一个copy,你能实现吗?如果我让你实现引用计数的功能,你有思路吗?)。因而本文并 不适用于 专注业务层快速开发的同学,因为这里将贴有大量的源码。没有耐心的同学可以先收藏暂时回避一下,日后造火箭造飞机的时候再来。
核心问题iOS开发者都知道OC里面的内存管理是通过对象的引用计数来管理的,或手动MRC,或自动ARC,有些操作可以让引用计数加1,有些可以减1,一旦一个对象的引用计数为0,就回收内存了。
可是,你仅仅知道这里就行了吗?指望你能造火箭造飞机的面试官可不这么想了,比如问你一句,一个对象的 引用计数本身 保存在哪里??不关注底层的面试者,这时候可能会懵逼。
研究方式这篇文章不同于其它文章通过 clang编译 一个类文件以查看它的实现原理(笔者曾用clang编译分析Block的原理,传送门),而是直接通过下载runtime的源码来查看分析。
依据版本苹果开源了runtime的代码,查看的方式既可以通过 在线网页版 预览,也可以 下载归档文件 到本地查看。本篇文件讨论的版本是 objc4-723
目录

    • 类与对象
    • 1.1 对象 -- Object
    • 1.2 对象 -- NSObject
    • 1.3 对象 -- objc_object
    • 1.4 类 -- objc_class
    • 1.5 NSObject,objc_object,objc_class 三者的关系

    • 手动引用对引用计数的影响 -- retain操作
    • 2.1 两种对象:NSObject与Object的引用增加
    • 2.2 归根结底 -- NSObject对象的rootRetain()

    • isa与Tagged Pointer
    • 3.1 NSObject的唯一成员变量 -- isa
    • 3.2 isa_t联合体里面的数据含义
    • 3.3 isa_t联合体里面的宏
    • 3.4 是否Tagged Pointer的判断
    • 3.5 与isa类型有关的宏
    • 3.6 怎么判断是否支持优化的isa指针?-- 看设备、自己设置。
    • 3.7 怎么判断是否Tagged Pointer的对象?-- 看对象、自己设置
    • 3.8 引用计数的存储形式 -- 散列表

    • 散列表
  • 4.1 增加引用计数 -- sidetable_retain()
  • 4.2 增加引用计数 -- sidetable_tryRetain()
  • 4.3 获取散列表 -- SideTable()

    • 设置变量导致的引用计数变化 -- objc_retain操作
    • 5.1 情况1
    • 5.2 情况2
    • 5.3 objc_storeStrong导致的retain

    • 新建对象(分配内存与初始化)导致的引用计数变化 -- alloc 和 init 操作
    • 6.1 分配内存 -- alloc
    • 6.2 初始化 -- init

    • 获取引用计数

    • 结论

    • 拓展阅读
1. 类与对象下载完工程,打开查看


module.modulemap头文件描述文件
module ObjectiveC [system] [extern_c] {  umbrella "."  export *  module * {     export *  }  module NSObject {    requires objc    header "NSObject.h"    export *  }#if defined(BUILD_FOR_OSX)  module List {    // Uses @defs, which does not work in ObjC++ or non-ARC.    requires objc, !objc_arc, !cplusplus    header "List.h"    export *  }  module Object {    requires objc    header "Object.h"    export *  }  module Protocol {    requires objc    header "Protocol.h"    export *  }#endif#if !defined(BUILD_FOR_OSX)  // These file are not available outside macOS.  exclude header "hashtable.h"  exclude header "hashtable2.h"#endif}复制代码这里的Module本质上是一个描述文件,用来描述Module中包涵的内容,每个Module中必须包涵一个umbrella头文件,这个文件用来#import所有这个Module下的文件,比如#import <UIKit/UIKit.h>这个UIKit.h就是一个umbrella文件。关于Module更多参考 这篇文章
从#if defined(BUILD_FOR_OSX)这句逻辑判断可知, Object是针对macOS的,iOS开发暂时只关心NSObject即可。
1.1 对象 -- Object
Object.mmObject
#include "objc-private.h"#undef id#undef Classtypedef struct objc_class *Class;typedef struct objc_object *id;#if __OBJC2____OSX_AVAILABLE(10.0) __IOS_UNAVAILABLE __TVOS_UNAVAILABLE__WATCHOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLEOBJC_ROOT_CLASS@interface Object {     Class isa; } @end@implementation Object+ (id)initialize{    return self; }+ (id)class{    return self;}-(id) retain{    return _objc_rootRetain(self);}-(void) release{    _objc_rootRelease(self);}-(id) autorelease{    return _objc_rootAutorelease(self);}+(id) retain{    return self;}+(void) release{}+(id) autorelease{    return self;}@end复制代码1.2 对象 -- NSObject
NSObject.hNSObject
#ifndef _OBJC_NSOBJECT_H_#define _OBJC_NSOBJECT_H_#if __OBJC__#include <objc/objc.h>#include <objc/NSObjCRuntime.h>@class NSString, NSMethodSignature, NSInvocation;@protocol NSObject- (BOOL)isEqual:(id)object;@property (readonly) NSUInteger hash;@property (readonly) Class superclass;- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'type(of: anObject)' instead");- (instancetype)self;- (id)performSelector:(SEL)aSelector;- (id)performSelector:(SEL)aSelector withObject:(id)object;- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;- (BOOL)isProxy;- (BOOL)isKindOfClass:(Class)aClass;- (BOOL)isMemberOfClass:(Class)aClass;- (BOOL)conformsToProtocol:(Protocol *)aProtocol;- (BOOL)respondsToSelector:(SEL)aSelector;- (instancetype)retain OBJC_ARC_UNAVAILABLE;- (oneway void)release OBJC_ARC_UNAVAILABLE;- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;- (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;@property (readonly, copy) NSString *description;@optional@property (readonly, copy) NSString *debugDescription;@endOBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)OBJC_ROOT_CLASSOBJC_EXPORT@interface NSObject <NSObject> {#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wobjc-interface-ivars"    Class isa  OBJC_ISA_AVAILABILITY;#pragma clang diagnostic pop}+ (void)load;+ (void)initialize;- (instancetype)init#if NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER    NS_DESIGNATED_INITIALIZER#endif    ;+ (instancetype)new OBJC_SWIFT_UNAVAILABLE("use object initializers instead");+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead");+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer");- (void)finalize OBJC_DEPRECATED("Objective-C garbage collection is no longer supported");- (id)copy;- (id)mutableCopy;+ (id)copyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;+ (id)mutableCopyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;+ (BOOL)instancesRespondToSelector:(SEL)aSelector;+ (BOOL)conformsToProtocol:(Protocol *)protocol;- (IMP)methodForSelector:(SEL)aSelector;+ (IMP)instanceMethodForSelector:(SEL)aSelector;- (void)doesNotRecognizeSelector:(SEL)aSelector;- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");- (BOOL)allowsWeakReference UNAVAILABLE_ATTRIBUTE;- (BOOL)retainWeakReference UNAVAILABLE_ATTRIBUTE;+ (BOOL)isSubclassOfClass:(Class)aClass;+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);+ (NSUInteger)hash;+ (Class)superclass;+ (Class)class OBJC_SWIFT_UNAVAILABLE("use 'aClass.self' instead");+ (NSString *)description;+ (NSString *)debugDescription;@end#endif#endif复制代码1.3 对象 -- objc_object关键信息
  • isa: isa_t类型的指针,详情可见下面3.2节。简单的说,它是这样的一个联合体,包含了bits (是一个 uintptr_t 类型的值,作为isa初始化列表中必初始化的值,可以用来获取isa结构体)和 cls (该变量会指向对象所属的类的结构,在 64 位设备上会占用 8byte)。
objc-private.hobjc_object
struct objc_object {private:    isa_t isa;public:    // ISA() assumes this is NOT a tagged pointer object    Class ISA();    // getIsa() allows this to be a tagged pointer object    Class getIsa();    // initIsa() should be used to init the isa of new objects only.    // If this object already has an isa, use changeIsa() for correctness.    // initInstanceIsa(): objects with no custom RR/AWZ    // initClassIsa(): class objects    // initProtocolIsa(): protocol objects    // initIsa(): other objects    void initIsa(Class cls /*nonpointer=false*/);    void initClassIsa(Class cls /*nonpointer=maybe*/);    void initProtocolIsa(Class cls /*nonpointer=maybe*/);    void initInstanceIsa(Class cls, bool hasCxxDtor);    // changeIsa() should be used to change the isa of existing objects.    // If this is a new object, use initIsa() for performance.    Class changeIsa(Class newCls);    bool hasNonpointerIsa();    bool isTaggedPointer();    bool isBasicTaggedPointer();    bool isExtTaggedPointer();    bool isClass();    // object may have associated objects?    bool hasAssociatedObjects();    void setHasAssociatedObjects();    // object may be weakly referenced?    bool isWeaklyReferenced();    void setWeaklyReferenced_nolock();    // object may have -.cxx_destruct implementation?    bool hasCxxDtor();    // Optimized calls to retain/release methods    id retain();    void release();    id autorelease();    // Implementations of retain/release methods    id rootRetain();    bool rootRelease();    id rootAutorelease();    bool rootTryRetain();    bool rootReleaseShouldDealloc();    uintptr_t rootRetainCount();    // Implementation of dealloc methods    bool rootIsDeallocating();    void clearDeallocating();    void rootDealloc();private:    void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);    // Slow paths for inline control    id rootAutorelease2();    bool overrelease_error();#if SUPPORT_NONPOINTER_ISA    // Unified retain count manipulation for nonpointer isa    id rootRetain(bool tryRetain, bool handleOverflow);    bool rootRelease(bool performDealloc, bool handleUnderflow);    id rootRetain_overflow(bool tryRetain);    bool rootRelease_underflow(bool performDealloc);    void clearDeallocating_slow();    // Side table retain count overflow for nonpointer isa    void sidetable_lock();    void sidetable_unlock();    void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);    bool sidetable_addExtraRC_nolock(size_t delta_rc);    size_t sidetable_subExtraRC_nolock(size_t delta_rc);    size_t sidetable_getExtraRC_nolock();#endif    // Side-table-only retain count    bool sidetable_isDeallocating();    void sidetable_clearDeallocating();    bool sidetable_isWeaklyReferenced();    void sidetable_setWeaklyReferenced_nolock();    id sidetable_retain();    id sidetable_retain_slow(SideTable& table);    uintptr_t sidetable_release(bool performDealloc = true);    uintptr_t sidetable_release_slow(SideTable& table, bool performDealloc = true);    bool sidetable_tryRetain();    uintptr_t sidetable_retainCount();#if DEBUG    bool sidetable_present();#endif};复制代码1.4 类 -- objc_class关键信息
  • isa: 继承于objc_object
  • superclass: 指向自己父类的指针
  • cache: 方法缓存
  • bits: 它是一个class_data_bits_t类型的指针。作为本类的实例方法链表。
注意区别
这里的bits是class_data_bits_t类型的,上一节objc_object的isa_t类型数据中也有一个uintptr_t类型的bits,但是这是两种结构。


由此可见,objc_class 继承于 objc_object, 所以也是包含一个isa的类。在OC里,不只是对象的实例包含一个isa,这个对象的类本身也有这么一个isa,类本身也是一个对象。
objc-runtime-new.hobjc_class
struct objc_class : objc_object {    // Class ISA;    Class superclass;    cache_t cache;             // formerly cache pointer and vtable    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags    class_rw_t *data() {         return bits.data();    }    void setData(class_rw_t *newData) {        bits.setData(newData);    }    void setInfo(uint32_t set) {        assert(isFuture()  ||  isRealized());        data()->setFlags(set);    }    void clearInfo(uint32_t clear) {        assert(isFuture()  ||  isRealized());        data()->clearFlags(clear);    }    // set and clear must not overlap    void changeInfo(uint32_t set, uint32_t clear) {        assert(isFuture()  ||  isRealized());        assert((set & clear) == 0);        data()->changeFlags(set, clear);    }    bool hasCustomRR() {        return ! bits.hasDefaultRR();    }    void setHasDefaultRR() {        assert(isInitializing());        bits.setHasDefaultRR();    }    void setHasCustomRR(bool inherited = false);    void printCustomRR(bool inherited);    bool hasCustomAWZ() {        return ! bits.hasDefaultAWZ();    }    void setHasDefaultAWZ() {        assert(isInitializing());        bits.setHasDefaultAWZ();    }    void setHasCustomAWZ(bool inherited = false);    void printCustomAWZ(bool inherited);    bool instancesRequireRawIsa() {        return bits.instancesRequireRawIsa();    }    void setInstancesRequireRawIsa(bool inherited = false);    void printInstancesRequireRawIsa(bool inherited);    bool canAllocNonpointer() {        assert(!isFuture());        return !instancesRequireRawIsa();    }    bool canAllocFast() {        assert(!isFuture());        return bits.canAllocFast();    }    bool hasCxxCtor() {        // addSubclass() propagates this flag from the superclass.        assert(isRealized());        return bits.hasCxxCtor();    }    void setHasCxxCtor() {         bits.setHasCxxCtor();    }    bool hasCxxDtor() {        // addSubclass() propagates this flag from the superclass.        assert(isRealized());        return bits.hasCxxDtor();    }    void setHasCxxDtor() {         bits.setHasCxxDtor();    }    bool isSwift() {        return bits.isSwift();    }    // Return YES if the class's ivars are managed by ARC,     // or the class is MRC but has ARC-style weak ivars.    bool hasAutomaticIvars() {        return data()->ro->flags & (RO_IS_ARC | RO_HAS_WEAK_WITHOUT_ARC);    }    // Return YES if the class's ivars are managed by ARC.    bool isARC() {        return data()->ro->flags & RO_IS_ARC;    }#if SUPPORT_NONPOINTER_ISA    // Tracked in non-pointer isas; not tracked otherwise#else    bool instancesHaveAssociatedObjects() {        // this may be an unrealized future class in the CF-bridged case        assert(isFuture()  ||  isRealized());        return data()->flags & RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS;    }    void setInstancesHaveAssociatedObjects() {        // this may be an unrealized future class in the CF-bridged case        assert(isFuture()  ||  isRealized());        setInfo(RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS);    }#endif    bool shouldGrowCache() {        return true;    }    void setShouldGrowCache(bool) {        // fixme good or bad for memory use?    }    bool isInitializing() {        return getMeta()->data()->flags & RW_INITIALIZING;    }    void setInitializing() {        assert(!isMetaClass());        ISA()->setInfo(RW_INITIALIZING);    }    bool isInitialized() {        return getMeta()->data()->flags & RW_INITIALIZED;    }    void setInitialized();    bool isLoadable() {        assert(isRealized());        return true;  // any class registered for +load is definitely loadable    }    IMP getLoadMethod();    // Locking: To prevent concurrent realization, hold runtimeLock.    bool isRealized() {        return data()->flags & RW_REALIZED;    }    // Returns true if this is an unrealized future class.    // Locking: To prevent concurrent realization, hold runtimeLock.    bool isFuture() {         return data()->flags & RW_FUTURE;    }    bool isMetaClass() {        assert(this);        assert(isRealized());        return data()->ro->flags & RO_META;    }    // NOT identical to this->ISA when this is a metaclass    Class getMeta() {        if (isMetaClass()) return (Class)this;        else return this->ISA();    }    bool isRootClass() {        return superclass == nil;    }    bool isRootMetaclass() {        return ISA() == (Class)this;    }    const char *mangledName() {         // fixme can't assert locks here        assert(this);        if (isRealized()  ||  isFuture()) {            return data()->ro->name;        } else {            return ((const class_ro_t *)data())->name;        }    }        const char *demangledName(bool realize = false);    const char *nameForLogging();    // May be unaligned depending on class's ivars.    uint32_t unalignedInstanceStart() {        assert(isRealized());        return data()->ro->instanceStart;    }    // Class's instance start rounded up to a pointer-size boundary.    // This is used for ARC layout bitmaps.    uint32_t alignedInstanceStart() {        return word_align(unalignedInstanceStart());    }    // May be unaligned depending on class's ivars.    uint32_t unalignedInstanceSize() {        assert(isRealized());        return data()->ro->instanceSize;    }    // Class's ivar size rounded up to a pointer-size boundary.    uint32_t alignedInstanceSize() {        return word_align(unalignedInstanceSize());    }    size_t instanceSize(size_t extraBytes) {        size_t size = alignedInstanceSize() + extraBytes;        // CF requires all objects be at least 16 bytes.        if (size < 16) size = 16;        return size;    }    void setInstanceSize(uint32_t newSize) {        assert(isRealized());        if (newSize != data()->ro->instanceSize) {            assert(data()->flags & RW_COPIED_RO);            *const_cast<uint32_t *>(&data()->ro->instanceSize) = newSize;        }        bits.setFastInstanceSize(newSize);    }    void chooseClassArrayIndex();    void setClassArrayIndex(unsigned Idx) {        bits.setClassArrayIndex(Idx);    }    unsigned classArrayIndex() {        return bits.classArrayIndex();    }};复制代码1.5 NSObject,objc_object,objc_class 三者的关系1)NSObject与objc_classNSObject有一个Class类型,名为isa成员变量


继续查看Class的本质,可以发现Class 其实就是 C 语言定义的结构体类型(struct objc_class)的指针,这个声明说明 Objective-C 的 实际上就是 struct objc_class


另外,第二个定义是经常遇到的 id 类型,这里可以看出 id 类型是 C 语言定义的结构体类型(struct objc_object)的指针,我们知道我们可以用 id 来声明一个对象,所以这也说明了 Objective-C 的 对象 实际上就是 struct objc_object
2)objc_object与objc_class继续查看objc_class的本质,可以发现objc_class是一个 继承 自objc_object的结构体。所以 Objective-C 中的 自身也是一个 对象,只是除了 objc_object 中定义的成员变量外,还有另外三个成员变量:superclass、cache 和 bits。


注意,这里面的 “结构体” 并非 C语言 里面的结构体,而是 C++语言 里面的结构体,而且这个概念仅限字面意思的结构体。严格来讲,其实struct关键字定义的是 ,跟class关键字定义的类除了默认访问权限的区别,没有区别。这一点,国内人写的C++书籍却很少有注意到。下面是比较权威的《C++ Primer》(第546页)一书关于这点的说明。


3)知识补课C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。下面简单列一下C++的struct跟C中的struct不一样的地方:
  • struct能包含成员函数
  • struct能继承
  • struct能实现多态
2. 手动引用对引用计数的影响 -- retain操作2.1 两种对象:NSObject与Object的引用增加① NSObject的retain
NSObject.mmretain
+ (id)retain {    return (id)self;}// Replaced by ObjectAlloc- (id)retain {    return ((id)self)->rootRetain();}复制代码② Object的retain
Object.mmretain
+(id) retain{    return self;}-(id) retain{    return _objc_rootRetain(self);}复制代码
NSObject.mm_objc_rootRetain(id obj)
id_objc_rootRetain(id obj){    assert(obj);    return obj->rootRetain();}复制代码可见,无论是NSObject还是Object的 retain,归根结底,调用的都是 objc_object 的 rootRetain()。
2.2 归根结底 -- NSObject对象的rootRetain()
objc4/objc4-723/runtime/objc-object.hobjc_object::rootRetain()
ALWAYS_INLINE id objc_object::rootRetain(){    return rootRetain(false, false);}复制代码
objc4/objc4-723/runtime/objc-object.hobjc_object::rootRetain(bool tryRetain, bool handleOverflow)
ALWAYS_INLINE id objc_object::rootRetain(bool tryRetain, bool handleOverflow){    if (isTaggedPointer()) return (id)this;    bool sideTableLocked = false;    bool transcribeToSideTable = false;    isa_t oldisa;    isa_t newisa;    do {        transcribeToSideTable = false;        oldisa = LoadExclusive(&isa.bits);        newisa = oldisa;        if (slowpath(!newisa.nonpointer)) {            ClearExclusive(&isa.bits);            if (!tryRetain && sideTableLocked) sidetable_unlock();            if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;            else return sidetable_retain();        }        // don't check newisa.fast_rr; we already called any RR overrides        if (slowpath(tryRetain && newisa.deallocating)) {            ClearExclusive(&isa.bits);            if (!tryRetain && sideTableLocked) sidetable_unlock();            return nil;        }        uintptr_t carry;        newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++        if (slowpath(carry)) {            // newisa.extra_rc++ overflowed            if (!handleOverflow) {                ClearExclusive(&isa.bits);                return rootRetain_overflow(tryRetain);            }            // Leave half of the retain counts inline and             // prepare to copy the other half to the side table.            if (!tryRetain && !sideTableLocked) sidetable_lock();            sideTableLocked = true;            transcribeToSideTable = true;            newisa.extra_rc = RC_HALF;            newisa.has_sidetable_rc = true;        }    } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));    if (slowpath(transcribeToSideTable)) {        // Copy the other half of the retain counts to the side table.        sidetable_addExtraRC_nolock(RC_HALF);    }    if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();    return (id)this;}复制代码其中,手动retain对引用计数的影响关键在这么一句话:
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++复制代码对isa的 extra_rc 变量进行+1,前面说到isa会存很多东西。
3. isa与Tagged Pointer3.1 NSObject的唯一成员变量 -- isa
NSObject.hNSObject的isa
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)OBJC_ROOT_CLASSOBJC_EXPORT@interface NSObject <NSObject> {#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wobjc-interface-ivars"    Class isa  OBJC_ISA_AVAILABILITY;#pragma clang diagnostic pop}复制代码其中,Class isa继续查看Class的定义:
objc-private.hClass
typedef struct objc_class *Class;typedef struct objc_object *id;复制代码其中,objc_object类内部结构:


其中,私有的成员数据isa为isa_t类型的联合体:
objc-private.hisa_t
union isa_t {    isa_t() { }    isa_t(uintptr_t value) : bits(value) { }    Class cls;    uintptr_t bits;#if SUPPORT_PACKED_ISA    // extra_rc must be the MSB-most field (so it matches carry/overflow flags)    // nonpointer must be the LSB (fixme or get rid of it)    // shiftcls must occupy the same bits that a real class pointer would    // bits + RC_ONE is equivalent to extra_rc + 1    // RC_HALF is the high bit of extra_rc (i.e. half of its range)    // future expansion:    // uintptr_t fast_rr : 1;     // no r/r overrides    // uintptr_t lock : 2;        // lock for atomic property, @synch    // uintptr_t extraBytes : 1;  // allocated with extra bytes# if __arm64__#   define ISA_MASK        0x0000000ffffffff8ULL#   define ISA_MAGIC_MASK  0x000003f000000001ULL#   define ISA_MAGIC_VALUE 0x000001a000000001ULL    struct {        uintptr_t nonpointer        : 1;        uintptr_t has_assoc         : 1;        uintptr_t has_cxx_dtor      : 1;        uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000        uintptr_t magic             : 6;        uintptr_t weakly_referenced : 1;        uintptr_t deallocating      : 1;        uintptr_t has_sidetable_rc  : 1;        uintptr_t extra_rc          : 19;#       define RC_ONE   (1ULL<<45)#       define RC_HALF  (1ULL<<18)    };# elif __x86_64__#   define ISA_MASK        0x00007ffffffffff8ULL#   define ISA_MAGIC_MASK  0x001f800000000001ULL#   define ISA_MAGIC_VALUE 0x001d800000000001ULL    struct {        uintptr_t nonpointer        : 1;        uintptr_t has_assoc         : 1;        uintptr_t has_cxx_dtor      : 1;        uintptr_t shiftcls          : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000        uintptr_t magic             : 6;        uintptr_t weakly_referenced : 1;        uintptr_t deallocating      : 1;        uintptr_t has_sidetable_rc  : 1;        uintptr_t extra_rc          : 8;#       define RC_ONE   (1ULL<<56)#       define RC_HALF  (1ULL<<7)    };# else#   error unknown architecture for packed isa# endif// SUPPORT_PACKED_ISA#endif#if SUPPORT_INDEXED_ISA# if  __ARM_ARCH_7K__ >= 2#   define ISA_INDEX_IS_NPI      1#   define ISA_INDEX_MASK        0x0001FFFC#   define ISA_INDEX_SHIFT       2#   define ISA_INDEX_BITS        15#   define ISA_INDEX_COUNT       (1 << ISA_INDEX_BITS)#   define ISA_INDEX_MAGIC_MASK  0x001E0001#   define ISA_INDEX_MAGIC_VALUE 0x001C0001    struct {        uintptr_t nonpointer        : 1;        uintptr_t has_assoc         : 1;        uintptr_t indexcls          : 15;        uintptr_t magic             : 4;        uintptr_t has_cxx_dtor      : 1;        uintptr_t weakly_referenced : 1;        uintptr_t deallocating      : 1;        uintptr_t has_sidetable_rc  : 1;        uintptr_t extra_rc          : 7;#       define RC_ONE   (1ULL<<25)#       define RC_HALF  (1ULL<<6)    };# else#   error unknown architecture for indexed isa# endif// SUPPORT_INDEXED_ISA#endif};复制代码其中,cls 变量会指向对象所属的类的结构,在 64 位设备上会占用 8byte。
另外,bits 变量保存着isa的唯一标志(可以根据bits获取isa),是一个类型为 uintptr_t 的数据, uintptr_t的定义:
typedef unsigned long                uintptr_t;复制代码知识回顾不熟悉C++的朋友可能很难看出来bits会是如何初始化的,其实,这是一种与构造函数并列的初始化办法 -- 初始化列表。关于初始化列表的定义,截取百度百科的一段话:


所以,再回过来看bits,bits以isa_t(uintptr_t value)中的value为初始化的值:


例如isa初始化的API objc_object::initIsa(Class cls)`中,有这样一句:
isa_t newisa(0);newisa.bits = ISA_INDEX_MAGIC_VALUE;//...复制代码而这个bits值可以用来获取isa(注意区分左右两边的bits分别是两个东西):
isa_t bits = LoadExclusive(&isa.bits);复制代码其中,LoadExclusive根据平台的不同,实现体并不一样,这是__arm64__平台的实现体:
#if __arm64__static ALWAYS_INLINEuintptr_t LoadExclusive(uintptr_t *src){    uintptr_t result;    asm("ldxr %x0, [%x1]"         : "=r" (result)         : "r" (src), "m" (*src));    return result;}复制代码对这个isa (这里是左边的bits,它是个isa,而非右边的uintptr_t) 的调用,比如获取引用计数的源代码中就有几处:
inline uintptr_t objc_object::rootRetainCount(){    if (isTaggedPointer()) return (uintptr_t)this;    sidetable_lock();    isa_t bits = LoadExclusive(&isa.bits);    ClearExclusive(&isa.bits);    if (bits.nonpointer) {        uintptr_t rc = 1 + bits.extra_rc;        if (bits.has_sidetable_rc) {            rc += sidetable_getExtraRC_nolock();        }        sidetable_unlock();        return rc;    }    sidetable_unlock();    return sidetable_retainCount();}复制代码调用的有:bits.extra_rcbits.nonpointerbits.has_sidetable_rc
3.2 isa_t联合体里面struct的数据含义nonpointer该变量占用 1bit 内存空间,可以有两个值:0 和 1,分别代表不同的 isa_t 的类型:
  • 0 表示 isa_t 没有开启指针优化,不使用 isa_t 中定义的结构体。访问 objc_object 的 isa 会直接返回 isa_t 结构中的 cls 变量,cls 变量会指向对象所属的类的结构,在 64 位设备上会占用 8byte。
  • 1 表示 isa_t 开启了指针优化,不能直接访问 objc_object 的 isa 成员变量 (因为 isa 已经不是一个合法的内存指针了,而是一个 Tagged Pointer ),从其名字 nonpointer 也可获知这个 isa 已经不是一个指针了。但是 isa 中包含了类信息、对象的引用计数等信息,在 64 位设备上充分利用了内存空间。

shiftcls存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针。
has_assoc该变量与对象的关联引用有关,当对象有关联引用时,释放对象时需要做额外的逻辑。关联引用就是我们通常用 objc_setAssociatedObject 方法设置给对象的,这里对于关联引用不做过多分析,如果后续有时间写关联引用实现时再深入分析关联引用有关的代码。
has_cxx_dtor表示该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象。
magic用于判断对象是否已经完成了初始化,在 arm64 中 0x16 是调试器判断当前对象是真的对象还是没有初始化的空间(在 x86_64 中该值为 0x3b)。
weakly_referenced标志对象是否被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放。
deallocating标志对象是否正在释放内存。
extra_rc表示该对象的引用计数值,实际上是引用计数值减 1,例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10,则需要使用到下面的 has_sidetable_rc。
has_sidetable_rc当对象引用计数大于 10 时,则has_sidetable_rc 的值为 1,那么引用计数会存储在一个叫 SideTable 的类的属性中,这是一个散列表。
ISA_MAGIC_MASK通过掩码方式获取 magic 值。
ISA_MASK通过掩码方式获取 isa 的类指针值。
RC_ONE 和 RC_HALF用于引用计数的相关计算。
3.3 isa_t联合体里面的宏
SUPPORT_PACKED_ISA
表示平台是否支持在 isa 指针中插入除 Class 之外的信息。
  • 如果支持就会将 Class 信息放入 isa_t 定义的 struct 内,并附上一些其他信息,例如上面的 nonpointer 等等;
  • 如果不支持,那么不会使用 isa_t 内定义的 struct,这时 isa_t 只使用 cls(Class 指针)。
小结在 iOS 以及 MacOSX 设备上,SUPPORT_PACKED_ISA 定义为 1
__arm64__、__x86_64__
表示 CPU 架构,例如电脑一般是 __x86_64__ 架构,手机一般是 arm 结构,这里 64 代表是 64 位 CPU。上面只列出了 __arm64__ 架构的定义。
小结iOS 设备上 __arm64__ 是 1
SUPPORT_INDEXED_ISA
SUPPORT_INDEXED_ISA 表示 isa_t 中存放的 Class 信息是 Class 的地址,还是一个索引(根据该索引可在类信息表中查找该类结构地址)。可以看出,多了一个 uintptr_t indexcls : 15;。
小结iOS 设备上 SUPPORT_INDEXED_ISA 是 0
3.4 是否Tagged Pointer的判断
objc-object.hobjc_object::isTaggedPointer()
inline bool objc_object::isTaggedPointer() {    return _objc_isTaggedPointer(this);}复制代码
objc-internal.h_objc_isTaggedPointer(const void * _Nullable ptr)
static inline bool _objc_isTaggedPointer(const void * _Nullable ptr) {    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;}复制代码3.5 与isa类型有关的宏
SUPPORT_NONPOINTER_ISA
用于标记是否支持优化的 isa 指针,其字面含义意思是 isa 的内容不再是类的指针了,而是包含了更多信息,比如引用计数,析构状态,被其他 weak 变量引用情况。下面看看SUPPORT_NONPOINTER_ISA及其相关宏的定义:
objc-config.hSUPPORT_TAGGED_POINTERS
// Define SUPPORT_TAGGED_POINTERS=1 to enable tagged pointer objects// Be sure to edit tagged pointer SPI in objc-internal.h as well.#if !(__OBJC2__  &&  __LP64__)#   define SUPPORT_TAGGED_POINTERS 0#else#   define SUPPORT_TAGGED_POINTERS 1#endif// Define SUPPORT_MSB_TAGGED_POINTERS to use the MSB // as the tagged pointer marker instead of the LSB.// Be sure to edit tagged pointer SPI in objc-internal.h as well.#if !SUPPORT_TAGGED_POINTERS  ||  !TARGET_OS_IPHONE#   define SUPPORT_MSB_TAGGED_POINTERS 0#else#   define SUPPORT_MSB_TAGGED_POINTERS 1#endif// Define SUPPORT_INDEXED_ISA=1 on platforms that store the class in the isa // field as an index into a class table.// Note, keep this in sync with any .s files which also define it.// Be sure to edit objc-abi.h as well.#if __ARM_ARCH_7K__ >= 2#   define SUPPORT_INDEXED_ISA 1#else#   define SUPPORT_INDEXED_ISA 0#endif// Define SUPPORT_PACKED_ISA=1 on platforms that store the class in the isa // field as a maskable pointer with other data around it.#if (!__LP64__  ||  TARGET_OS_WIN32  ||  TARGET_OS_SIMULATOR)#   define SUPPORT_PACKED_ISA 0#else#   define SUPPORT_PACKED_ISA 1#endif// Define SUPPORT_NONPOINTER_ISA=1 on any platform that may store something// in the isa field that is not a raw pointer.#if !SUPPORT_INDEXED_ISA  &&  !SUPPORT_PACKED_ISA#   define SUPPORT_NONPOINTER_ISA 0#else#   define SUPPORT_NONPOINTER_ISA 1#endif复制代码3.6 怎么判断是否支持优化的isa指针?-- 看设备、自己设置。
  • 已知iOS系统的SUPPORT_PACKED_ISA为1,SUPPORT_INDEXED_ISA为0,根据4.5节中源代码的定义可知,iOS系统的SUPPORT_NONPOINTER_ISA为1。
  • 在环境变量中设置OBJC_DISABLE_NONPOINTER_ISA。

即,iOS系统 支持 优化的isa指针
在 64 位环境下,优化的 isa 指针并不是就一定会存储引用计数,毕竟用 19bit (iOS 系统)保存引用计数不一定够。需要注意的是这 19 位保存的是引用计数的值减一。
3.7 怎么判断是否Tagged Pointer的对象?-- 看对象、自己设置
  • 可以启用Tagged Pointer的类对象有:NSDate、NSNumber、NSString。Tagged Pointer专门用来存储小的对象。
  • 在环境变量中设置OBJC_DISABLE_TAGGED_POINTERS=YES强制不启用Tagged Pointer。

3.8 引用计数的存储形式 -- 散列表下面对sidetable_retain进行分析。
4. 散列表4.1 增加引用计数 -- sidetable_retain()第2节的增加引用假设,以及后面第8节的获取引用计数会用到下面的API:
NSObject.mmobjc_object::sidetable_retain()
idobjc_object::sidetable_retain(){#if SUPPORT_NONPOINTER_ISA    assert(!isa.nonpointer);#endif    SideTable& table = SideTables()[this];        table.lock();    size_t& refcntStorage = table.refcnts[this];    if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {        refcntStorage += SIDE_TABLE_RC_ONE;    }    table.unlock();    return (id)this;}复制代码4.2 增加引用计数 -- sidetable_tryRetain()
NSObject.mmobjc_object::sidetable_tryRetain()
boolobjc_object::sidetable_tryRetain(){#if SUPPORT_NONPOINTER_ISA    assert(!isa.nonpointer);#endif    SideTable& table = SideTables()[this];    // NO SPINLOCK HERE    // _objc_rootTryRetain() is called exclusively by _objc_loadWeak(),     // which already acquired the lock on our behalf.    // fixme can't do this efficiently with os_lock_handoff_s    // if (table.slock == 0) {    //     _objc_fatal("Do not call -_tryRetain.");    // }    bool result = true;    RefcountMap::iterator it = table.refcnts.find(this);    if (it == table.refcnts.end()) {        table.refcnts[this] = SIDE_TABLE_RC_ONE;    } else if (it->second & SIDE_TABLE_DEALLOCATING) {        result = false;    } else if (! (it->second & SIDE_TABLE_RC_PINNED)) {        it->second += SIDE_TABLE_RC_ONE;    }        return result;}复制代码4.3 获取散列表 -- SideTable()
NSObject.mmSideTable
struct SideTable {    spinlock_t slock;    RefcountMap refcnts;    weak_table_t weak_table;    SideTable() {        memset(&weak_table, 0, sizeof(weak_table));    }    ~SideTable() {        _objc_fatal("Do not delete SideTable.");    }    void lock() { slock.lock(); }    void unlock() { slock.unlock(); }    void forceReset() { slock.forceReset(); }    // Address-ordered lock discipline for a pair of side tables.    template<HaveOld, HaveNew>    static void lockTwo(SideTable *lock1, SideTable *lock2);    template<HaveOld, HaveNew>    static void unlockTwo(SideTable *lock1, SideTable *lock2);};复制代码其中,RefcountMap以及HaveOld,HaveNew的定义为:
// RefcountMap disguises its pointers because we // don't want the table to act as a root for `leaks`.typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;// Template parameters.enum HaveOld { DontHaveOld = false, DoHaveOld = true };enum HaveNew { DontHaveNew = false, DoHaveNew = true };复制代码
llvm-DenseMap.hDenseMap/DenseMapBase


DenseMapBase


DenseMap


5. 设置变量导致的引用计数变化 -- objc_retain操作5.1 情况1 -- strong
runtime.h设置strong变量
/**  * Sets the value of an instance variable in an object. *  * @param obj The object containing the instance variable whose value you want to set. * @param ivar The Ivar describing the instance variable whose value you want to set. * @param value The new value for the instance variable. *  * @note Instance variables with known memory management (such as ARC strong and weak) *  use that memory management. Instance variables with unknown memory management  *  are assigned as if they were strong. * @note \c object_setIvar is faster than \c object_setInstanceVariable if the Ivar *  for the instance variable is already known. */OBJC_EXPORT voidobject_setIvarWithStrongDefault(id _Nullable obj, Ivar _Nonnull ivar,                                id _Nullable value)     OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);复制代码
objc-class.mmobject_setIvarWithStrongDefault
void object_setIvarWithStrongDefault(id obj, Ivar ivar, id value){    return _object_setIvar(obj, ivar, value, true /*strong default*/);}复制代码
objc-class.mm_object_setIvar
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");    }}复制代码
NSObject.mmobjc_storeStrong
voidobjc_storeStrong(id *location, id obj){    id prev = *location;    if (obj == prev) {        return;    }    objc_retain(obj);    *location = obj;    objc_release(prev);}复制代码5.2 情况2 -- weak
objc-class.mm设置weak变量
object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value)     OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);复制代码
objc-class.mmobject_setIvar
void object_setIvar(id obj, Ivar ivar, id value){    return _object_setIvar(obj, ivar, value, false /*not strong default*/);}复制代码
  • 可见,这里同样调用了 _object_setIvar,代码情况1,是同一个API。其中,不同于objc_storeStrong,走的是objc_storeWeak,下面分析一下:
NSObject.mmobjc_storeWeak
/**  * This function stores a new value into a __weak variable. It would * be used anywhere a __weak variable is the target of an assignment. *  * @param location The address of the weak pointer itself * @param newObj The new object this weak ptr should now point to *  * @return \e newObj */idobjc_storeWeak(id *location, id newObj){    return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>        (location, (objc_object *)newObj);}复制代码上面有一个storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating> (location, (objc_object *)newObj),它的代码有点长,核心的关键是更新了weak哈希表:->weak_table。读者可以从下面搜索一下这个关键词的位置。
// Update a weak variable.// If HaveOld is true, the variable has an existing value //   that needs to be cleaned up. This value might be nil.// If HaveNew is true, there is a new value that needs to be //   assigned into the variable. This value might be nil.// If CrashIfDeallocating is true, the process is halted if newObj is //   deallocating or newObj's class does not support weak references. //   If CrashIfDeallocating is false, nil is stored instead.enum CrashIfDeallocating {    DontCrashIfDeallocating = false, DoCrashIfDeallocating = true};template <HaveOld haveOld, HaveNew haveNew,          CrashIfDeallocating crashIfDeallocating>static id storeWeak(id *location, objc_object *newObj){    assert(haveOld  ||  haveNew);    if (!haveNew) assert(newObj == nil);    Class previouslyInitializedClass = nil;    id oldObj;    SideTable *oldTable;    SideTable *newTable;    // Acquire locks for old and new values.    // Order by lock address to prevent lock ordering problems.     // Retry if the old value changes underneath us. retry:    if (haveOld) {        oldObj = *location;        oldTable = &SideTables()[oldObj];    } else {        oldTable = nil;    }    if (haveNew) {        newTable = &SideTables()[newObj];    } else {        newTable = nil;    }    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);    if (haveOld  &&  *location != oldObj) {        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);        goto retry;    }    // Prevent a deadlock between the weak reference machinery    // and the +initialize machinery by ensuring that no     // weakly-referenced object has an un-+initialized isa.    if (haveNew  &&  newObj) {        Class cls = newObj->getIsa();        if (cls != previouslyInitializedClass  &&              !((objc_class *)cls)->isInitialized())         {            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));            // If this class is finished with +initialize then we're good.            // If this class is still running +initialize on this thread             // (i.e. +initialize called storeWeak on an instance of itself)            // then we may proceed but it will appear initializing and             // not yet initialized to the check above.            // Instead set previouslyInitializedClass to recognize it on retry.            previouslyInitializedClass = cls;            goto retry;        }    }    // Clean up old value, if any.    if (haveOld) {        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);    }    // Assign new value, if any.    if (haveNew) {        newObj = (objc_object *)            weak_register_no_lock(&newTable->weak_table, (id)newObj, location,                                   crashIfDeallocating);        // weak_register_no_lock returns nil if weak store should be rejected        // Set is-weakly-referenced bit in refcount table.        if (newObj  &&  !newObj->isTaggedPointer()) {            newObj->setWeaklyReferenced_nolock();        }        // Do not set *location anywhere else. That would introduce a race.        *location = (id)newObj;    }    else {        // No new value. The storage is not changed.    }        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);    return (id)newObj;}复制代码5.3 objc_storeStrong导致的retain上面第5.1节中有一个objc_storeStrong,这里继续分析它的原理。
NSObject.mmobjc_storeStrong(id *location, id obj)
voidobjc_storeStrong(id *location, id obj){    id prev = *location;    if (obj == prev) {        return;    }    objc_retain(obj);    *location = obj;    objc_release(prev);}复制代码
NSObject.mmobjc_retain(id obj)
/************************************************************************ Optimized retain/release/autorelease entrypoints**********************************************************************/#if __OBJC2____attribute__((aligned(16)))id objc_retain(id obj){    if (!obj) return obj;    if (obj->isTaggedPointer()) return obj;    return obj->retain();}__attribute__((aligned(16)))void objc_release(id obj){    if (!obj) return;    if (obj->isTaggedPointer()) return;    return obj->release();}__attribute__((aligned(16)))idobjc_autorelease(id obj){    if (!obj) return obj;    if (obj->isTaggedPointer()) return obj;    return obj->autorelease();}// OBJC2#else// not OBJC2id objc_retain(id obj) { return [obj retain]; }void objc_release(id obj) { [obj release]; }id objc_autorelease(id obj) { return [obj autorelease]; }#endif复制代码可知:1)如果TaggedPointer,则返回本身。2)如果非TaggedPointer,则由对象的retain()返回。
objc-object.hobjc_object::retain()
// Equivalent to calling [this retain], with shortcuts if there is no overrideinline id objc_object::retain(){    assert(!isTaggedPointer());    if (fastpath(!ISA()->hasCustomRR())) {        return rootRetain();    }    return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_retain);}复制代码
objc-object.hobjc_object::rootRetain()
// Base retain implementation, ignoring overrides.// This does not check isa.fast_rr; if there is an RR override then // it was already called and it chose to call [super retain].//// tryRetain=true is the -_tryRetain path.// handleOverflow=false is the frameless fast path.// handleOverflow=true is the framed slow path including overflow to side table// The code is structured this way to prevent duplication.ALWAYS_INLINE id objc_object::rootRetain(){    return rootRetain(false, false);}复制代码这里的rootRetain(false, false);正是上文第2.2节中介绍的,不再赘述。
6. 新建对象(分配内存与初始化)导致的引用计数变化 -- alloc 和 init 操作首先,新建一个对象的典型写法:
NSObject *obj = [NSObject alloc] init];复制代码6.1 分配内存 -- alloc+ (id)alloc {    return _objc_rootAlloc(self);}复制代码// Base class implementation of +alloc. cls is not nil.// Calls [cls allocWithZone:nil].id_objc_rootAlloc(Class cls){    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);}复制代码// Call [cls alloc] or [cls allocWithZone:nil], with appropriate // shortcutting optimizations.static ALWAYS_INLINE idcallAlloc(Class cls, bool checkNil, bool allocWithZone=false){    if (slowpath(checkNil && !cls)) return nil;#if __OBJC2__    if (fastpath(!cls->ISA()->hasCustomAWZ())) {        // No alloc/allocWithZone implementation. Go straight to the allocator.        // fixme store hasCustomAWZ in the non-meta class and         // add it to canAllocFast's summary        if (fastpath(cls->canAllocFast())) {            // No ctors, raw isa, etc. Go straight to the metal.            bool dtor = cls->hasCxxDtor();            id obj = (id)calloc(1, cls->bits.fastInstanceSize());            if (slowpath(!obj)) return callBadAllocHandler(cls);            obj->initInstanceIsa(cls, dtor);            return obj;        }        else {            // Has ctor or raw isa or something. Use the slower path.            id obj = class_createInstance(cls, 0);            if (slowpath(!obj)) return callBadAllocHandler(cls);            return obj;        }    }#endif    // No shortcuts available.    if (allocWithZone) return [cls allocWithZone:nil];    return [cls alloc];}复制代码分支1 -- obj->initInstanceIsa(cls, dtor);inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor){    assert(!cls->instancesRequireRawIsa());    assert(hasCxxDtor == cls->hasCxxDtor());    initIsa(cls, true, hasCxxDtor);}复制代码inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) {     assert(!isTaggedPointer());         if (!nonpointer) {        isa.cls = cls;    } else {        assert(!DisableNonpointerIsa);        assert(!cls->instancesRequireRawIsa());        isa_t newisa(0);#if SUPPORT_INDEXED_ISA        assert(cls->classArrayIndex() > 0);        newisa.bits = ISA_INDEX_MAGIC_VALUE;        // isa.magic is part of ISA_MAGIC_VALUE        // isa.nonpointer is part of ISA_MAGIC_VALUE        newisa.has_cxx_dtor = hasCxxDtor;        newisa.indexcls = (uintptr_t)cls->classArrayIndex();#else        newisa.bits = ISA_MAGIC_VALUE;        // isa.magic is part of ISA_MAGIC_VALUE        // isa.nonpointer is part of ISA_MAGIC_VALUE        newisa.has_cxx_dtor = hasCxxDtor;        newisa.shiftcls = (uintptr_t)cls >> 3;#endif        // This write must be performed in a single store in some cases        // (for example when realizing a class because other threads        // may simultaneously try to use the class).        // fixme use atomics here to guarantee single-store and to        // guarantee memory order w.r.t. the class index table        // ...but not too atomic because we don't want to hurt instantiation        isa = newisa;    }}复制代码上述代码中,newisa.bits = ISA_MAGIC_VALUE; 是为了对 isa 结构赋值一个初始值,通过对 isa_t 的结构分析,我们可以知道此次赋值只是对 nonpointer 和 magic 部分进行了赋值。
newisa.shiftcls = (uintptr_t)cls >> 3; 是将类的地址存储在对象的 isa 结构中。这里右移三位的主要原因是用于将 Class 指针中无用的后三位清除减小内存的消耗,因为类的指针要按照字节(8 bits)对齐内存,其指针后三位都是没有意义的 0。关于类指针对齐的详细解析可参考:从 NSObject 的初始化了解 isa
分支2 -- id obj = class_createInstance(cls, 0);id class_createInstance(Class cls, size_t extraBytes){    return _class_createInstanceFromZone(cls, extraBytes, nil);}复制代码/************************************************************************ class_createInstance* fixme* Locking: none**********************************************************************/static __attribute__((always_inline)) id_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,                               bool cxxConstruct = true,                               size_t *outAllocatedSize = nil){    if (!cls) return nil;    assert(cls->isRealized());    // Read class's info bits all at once for performance    bool hasCxxCtor = cls->hasCxxCtor();    bool hasCxxDtor = cls->hasCxxDtor();    bool fast = cls->canAllocNonpointer();    size_t size = cls->instanceSize(extraBytes);    if (outAllocatedSize) *outAllocatedSize = size;    id obj;    if (!zone  &&  fast) {        obj = (id)calloc(1, size);        if (!obj) return nil;        obj->initInstanceIsa(cls, hasCxxDtor);    }     else {        if (zone) {            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);        } else {            obj = (id)calloc(1, size);        }        if (!obj) return nil;        // Use raw pointer isa on the assumption that they might be         // doing something weird with the zone or RR.        obj->initIsa(cls);    }    if (cxxConstruct && hasCxxCtor) {        obj = _objc_constructOrFree(obj, cls);    }    return obj;}复制代码其中,有个 obj->initIsa(cls);,初始化isa的操作:
inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) {     assert(!isTaggedPointer());         if (!nonpointer) {        isa.cls = cls;    } else {        assert(!DisableNonpointerIsa);        assert(!cls->instancesRequireRawIsa());        isa_t newisa(0);#if SUPPORT_INDEXED_ISA        assert(cls->classArrayIndex() > 0);        newisa.bits = ISA_INDEX_MAGIC_VALUE;        // isa.magic is part of ISA_MAGIC_VALUE        // isa.nonpointer is part of ISA_MAGIC_VALUE        newisa.has_cxx_dtor = hasCxxDtor;        newisa.indexcls = (uintptr_t)cls->classArrayIndex();#else        newisa.bits = ISA_MAGIC_VALUE;        // isa.magic is part of ISA_MAGIC_VALUE        // isa.nonpointer is part of ISA_MAGIC_VALUE        newisa.has_cxx_dtor = hasCxxDtor;        newisa.shiftcls = (uintptr_t)cls >> 3;#endif        // This write must be performed in a single store in some cases        // (for example when realizing a class because other threads        // may simultaneously try to use the class).        // fixme use atomics here to guarantee single-store and to        // guarantee memory order w.r.t. the class index table        // ...but not too atomic because we don't want to hurt instantiation        isa = newisa;    }}复制代码可见,alloc的时候会初始化isa,并通过newisa(0)的初始化列表办法生成一个isa,并根据是否支持indexed isa分别设置.bits的值。
6.2 初始化 -- init- (id)init {    return _objc_rootInit(self);}复制代码id_objc_rootInit(id obj){    // In practice, it will be hard to rely on this function.    // Many classes do not properly chain -init calls.    return obj;}复制代码7. 获取引用计数
NSObject.mmretainCount
- (NSUInteger)retainCount {    return ((id)self)->rootRetainCount();}复制代码
objc-object.hobjc_object::rootRetainCount()
inline uintptr_t objc_object::rootRetainCount(){    if (isTaggedPointer()) return (uintptr_t)this;    sidetable_lock();    isa_t bits = LoadExclusive(&isa.bits);    ClearExclusive(&isa.bits);    if (bits.nonpointer) {        uintptr_t rc = 1 + bits.extra_rc;        if (bits.has_sidetable_rc) {            rc += sidetable_getExtraRC_nolock();        }        sidetable_unlock();        return rc;    }    sidetable_unlock();    return sidetable_retainCount();}复制代码可见,获取引用计数的关键在这么一句话:
uintptr_t rc = 1 + bits.extra_rc;复制代码bits.extra_rc表示引用计数减1。当然,这只针对情况1,即bits.nonpointer为1(开启了指针优化),且bits.has_sidetable_rc为0(表示不存储在散列表Side Table中,而存储在extra_rc中)。
  • 情况0 -- TaggedPointer
直接返回isa值本身。
  • 情况1 -- 非TaggedPointer,且开启了指针优化,且存储在extra_rc中
objc-os.hLoadExclusive(uintptr_t *src)
static ALWAYS_INLINEuintptr_t LoadExclusive(uintptr_t *src){    return *src;}复制代码
  • 情况2 -- 非TaggedPointer,且开启了指针优化,且存储在散列表中
NSObject.mmobjc_object::sidetable_getExtraRC_nolock()
size_t objc_object::sidetable_getExtraRC_nolock(){    assert(isa.nonpointer);    SideTable& table = SideTables()[this];    RefcountMap::iterator it = table.refcnts.find(this);    if (it == table.refcnts.end()) return 0;    else return it->second >> SIDE_TABLE_RC_SHIFT;}复制代码可见,其逻辑就是先从 SideTable 的静态方法获取当前实例对应的 SideTable 对象,其 refcnts 属性就是之前说的存储引用计数的散列表,这里将其类型简写为 RefcountMap:
typedef objc::DenseMap RefcountMap;复制代码然后在引用计数表中用迭代器查找当前实例对应的键值对,获取引用计数值,并在此基础上 +1 并将结果返回。这也就是为什么之前说引用计数表存储的值为实际引用计数减一。
需要注意的是为什么这里把键值对的值做了向右移位操作(it->second >> SIDE_TABLE_RC_SHIFT):
// The order of these bits is important.#define SIDE_TABLE_WEAKLY_REFERENCED (1UL<<0)#define SIDE_TABLE_DEALLOCATING      (1UL<<1)  // MSB-ward of weak bit#define SIDE_TABLE_RC_ONE            (1UL<<2)  // MSB-ward of deallocating bit#define SIDE_TABLE_RC_PINNED         (1UL<<(WORD_BITS-1))#define SIDE_TABLE_RC_SHIFT 2#define SIDE_TABLE_FLAG_MASK (SIDE_TABLE_RC_ONE-1)复制代码可以看出值的第一个 bit 表示该对象是否有过 weak 对象,如果没有,在析构释放内存时可以更快;第二个 bit 表示该对象是否正在析构。从第三个 bit 开始才是存储引用计数数值的地方。所以这里要做向右移两位的操作,而对引用计数的 +1 和 -1 可以使用 SIDE_TABLE_RC_ONE,还可以用 SIDE_TABLE_RC_PINNED 来判断是否引用计数值有可能溢出。
  • 情况3 -- 默认值
NSObject.mmobjc_object::sidetable_retainCount()
uintptr_tobjc_object::sidetable_retainCount(){    SideTable& table = SideTables()[this];    size_t refcnt_result = 1;        table.lock();    RefcountMap::iterator it = table.refcnts.find(this);    if (it != table.refcnts.end()) {        // this is valid for SIDE_TABLE_RC_PINNED too        refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;    }    table.unlock();    return refcnt_result;}复制代码8. 结论
  • 如果有些对象支持使用 TaggedPointer
  • 苹果会直接将对象的指针值作为引用计数返回。
  • 如果另外一些对象不支持使用 TaggedPointer
  • 如果当前设备是 64 位环境并且使用 Objective-C 2.0,那么会使用对象的 isa 指针一部分空间 (bits.extra_rc)来存储它的引用计数;
  • 否则 Runtime 会使用一张 散列表 (SideTables())来管理引用计数。


作者:陈满iOS
链接:https://juejin.im/post/5b4c59a55188251ac9767872



5 个回复

倒序浏览
奈斯,优秀
回复 使用道具 举报
牛牛牛!
回复 使用道具 举报
奈斯,很赞
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马