引言:这篇文章旨在从runtime源码中分析出 引用计数 值自己的保存位置,适合对底层原理有兴趣的朋友,或者面试造火箭的同窗(好比百度的面试官很是喜欢问底层原理:好,我知道你说了深浅复制的区别一大堆,若是我让你本身实现一个copy,你能实现吗?若是我让你实现引用计数的功能,你有思路吗?)。于是本文并 不适用于 专一业务层快速开发的同窗,由于这里将贴有大量的源码。没有耐心的同窗能够先收藏暂时回避一下,往后造火箭造飞机的时候再来。html
iOS开发者都知道OC里面的内存管理是经过对象的引用计数来管理的,或手动MRC,或自动ARC,有些操做可让引用计数加1,有些能够减1,一旦一个对象的引用计数为0,就回收内存了。c++
但是,你仅仅知道这里就好了吗?期望你能造火箭造飞机的面试官可不这么想了,好比问你一句,一个对象的 引用计数自己 保存在哪里??不关注底层的面试者,这时候可能会懵逼。git
这篇文章不一样于其它文章经过 clang编译 一个类文件以查看它的实现原理(笔者曾用clang编译分析Block的原理,传送门),而是直接经过下载runtime的源码来查看分析。github
苹果开源了runtime的代码,查看的方式既能够经过 在线网页版 预览,也能够 下载归档文件 到本地查看。本篇文件讨论的版本是 objc4-723。面试
下载完工程,打开查看缓存
module.modulemap 头文件描述文件bash
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更多参考 这篇文章。markdown
从#if defined(BUILD_FOR_OSX)
这句逻辑判断可知, Object是针对macOS的,iOS开发暂时只关心NSObject便可。数据结构
Object.mm Object架构
#include "objc-private.h" #undef id #undef Class typedef struct objc_class *Class; typedef struct objc_object *id; #if __OBJC2__ __OSX_AVAILABLE(10.0) __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE OBJC_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 复制代码
NSObject.h NSObject
#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; @end OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0) OBJC_ROOT_CLASS OBJC_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 复制代码
关键信息:
isa
: isa_t
类型的指针,详情可见下面3.2节。简单的说,它是这样的一个联合体,包含了bits
(是一个 uintptr_t
类型的值,做为isa初始化列表中必初始化的值,能够用来获取isa结构体)和 cls
(该变量会指向对象所属的类的结构,在 64 位设备上会占用 8byte)。objc-private.h objc_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 }; 复制代码
关键信息:
isa
: 继承于objc_objectsuperclass
: 指向本身父类的指针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.h objc_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(); } }; 复制代码
NSObject有一个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。
继续查看objc_class的本质,能够发现objc_class是一个 继承 自objc_object的结构体。因此 Objective-C 中的 类 自身也是一个 对象,只是除了 objc_object 中定义的成员变量外,还有另外三个成员变量:superclass、cache 和 bits。
注意,这里面的 “结构体” 并不是 C语言 里面的结构体,而是 C++语言 里面的结构体,并且这个概念仅限字面意思的结构体。严格来说,其实struct关键字定义的是 类,跟class关键字定义的类除了默认访问权限的区别,没有区别。这一点,国内人写的C++书籍却不多有注意到。下面是比较权威的《C++ Primer》(第546页)一书关于这点的说明。
C++中的struct对C中的struct进行了扩充,它已经再也不只是一个包含不一样数据类型的数据结构了,它已经获取了太多的功能。下面简单列一下C++的struct跟C中的struct不同的地方:
NSObject.mm retain
+ (id)retain { return (id)self; } // Replaced by ObjectAlloc - (id)retain { return ((id)self)->rootRetain(); } 复制代码
Object.mm retain
+(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()
。
objc4/objc4-723/runtime/objc-object.h objc_object::rootRetain()
ALWAYS_INLINE id objc_object::rootRetain() { return rootRetain(false, false); } 复制代码
objc4/objc4-723/runtime/objc-object.h objc_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会存不少东西。
NSObject.h NSObject的isa
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0) OBJC_ROOT_CLASS OBJC_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.h Class
typedef struct objc_class *Class;
typedef struct objc_object *id;
复制代码
其中,objc_object类内部结构:
其中,私有的成员数据isa为isa_t类型的联合体:
objc-private.h isa_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_INLINE uintptr_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_rc
bits.nonpointer
bits.has_sidetable_rc
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
用于引用计数的相关计算。
SUPPORT_PACKED_ISA
表示平台是否支持在 isa 指针中插入除 Class 以外的信息。
nonpointer
等等;小结:在 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。
objc-object.h objc_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; } 复制代码
SUPPORT_NONPOINTER_ISA
用于标记是否支持优化的 isa 指针,其字面含义意思是 isa 的内容再也不是类的指针了,而是包含了更多信息,好比引用计数,析构状态,被其余 weak 变量引用状况。下面看看SUPPORT_NONPOINTER_ISA
及其相关宏的定义:
objc-config.h SUPPORT_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 复制代码
已知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 位保存的是引用计数的值减一。
能够启用Tagged Pointer的类对象有:NSDate、NSNumber、NSString。Tagged Pointer专门用来存储小的对象。
在环境变量中设置OBJC_DISABLE_TAGGED_POINTERS=YES强制不启用Tagged Pointer。
下面对sidetable_retain
进行分析。
第2节的增长引用假设,以及后面第8节的获取引用计数会用到下面的API:
NSObject.mm objc_object::sidetable_retain()
id objc_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; } 复制代码
NSObject.mm objc_object::sidetable_tryRetain()
bool objc_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; } 复制代码
NSObject.mm SideTable
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.h DenseMap/DenseMapBase
DenseMapBase
DenseMap
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 void object_setIvarWithStrongDefault(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value) OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0); 复制代码
objc-class.mm object_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.mm objc_storeStrong
void objc_storeStrong(id *location, id obj) { id prev = *location; if (obj == prev) { return; } objc_retain(obj); *location = obj; objc_release(prev); } 复制代码
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.mm object_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.mm objc_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 */ id objc_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.1节中有一个objc_storeStrong
,这里继续分析它的原理。
NSObject.mm objc_storeStrong(id *location, id obj)
void objc_storeStrong(id *location, id obj) { id prev = *location; if (obj == prev) { return; } objc_retain(obj); *location = obj; objc_release(prev); } 复制代码
NSObject.mm objc_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))) id objc_autorelease(id obj) { if (!obj) return obj; if (obj->isTaggedPointer()) return obj; return obj->autorelease(); } // OBJC2 #else // not OBJC2 id 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.h objc_object::retain()
// Equivalent to calling [this retain], with shortcuts if there is no override inline id objc_object::retain() { assert(!isTaggedPointer()); if (fastpath(!ISA()->hasCustomRR())) { return rootRetain(); } return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_retain); } 复制代码
objc-object.h objc_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节中介绍的,再也不赘述。
首先,新建一个对象的典型写法:
NSObject *obj = [NSObject alloc] init];
复制代码
+ (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 id callAlloc(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]; } 复制代码
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 。
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
的值。
- (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; } 复制代码
NSObject.mm retainCount
- (NSUInteger)retainCount { return ((id)self)->rootRetainCount(); } 复制代码
objc-object.h objc_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
中)。
直接返回isa值自己。
extra_rc
中objc-os.h LoadExclusive(uintptr_t *src)
static ALWAYS_INLINE uintptr_t LoadExclusive(uintptr_t *src) { return *src; } 复制代码
NSObject.mm objc_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
来判断是否引用计数值有可能溢出。
NSObject.mm objc_object::sidetable_retainCount()
uintptr_t objc_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; } 复制代码
bits.extra_rc
)来存储它的引用计数;SideTables()
)来管理引用计数。https://www.jianshu.com/p/13c4fb1cedea