引言:这篇文章旨在从runtime源码中分析出 引用计数 值本身的保存位置,适合对底层原理有兴趣的朋友,或者面试造火箭的同学(比如百度的面试官非常喜欢问底层原理:好,我知道你说了深浅复制的区别一大堆,如果我让你自己实现一个copy,你能实现吗?如果我让你实现引用计数的功能,你有思路吗?)。因而本文并 不适用于 专注业务层快速开发的同学,因为这里将贴有大量的源码。没有耐心的同学可以先收藏暂时回避一下,日后造火箭造飞机的时候再来。核心问题iOS开发者都知道OC里面的内存管理是通过对象的引用计数来管理的,或手动MRC,或自动ARC,有些操作可以让引用计数加1,有些可以减1,一旦一个对象的引用计数为0,就回收内存了。
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更多参考 这篇文章。
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关键信息:
objc-private.hobjc_objectstruct 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关键信息:
objc-runtime-new.hobjc_classstruct 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成员变量
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()。
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对引用计数的影响关键在这么一句话:
NSObject.hNSObject的isaOBJC_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.hClasstypedef struct objc_class *Class;typedef struct objc_object *id;复制代码其中,objc_object类内部结构:
objc-private.hisa_tunion 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。
SUPPORT_PACKED_ISA表示平台是否支持在 isa 指针中插入除 Class 之外的信息。
__arm64__、__x86_64__表示 CPU 架构,例如电脑一般是 __x86_64__ 架构,手机一般是 arm 结构,这里 64 代表是 64 位 CPU。上面只列出了 __arm64__ 架构的定义。
SUPPORT_INDEXED_ISASUPPORT_INDEXED_ISA 表示 isa_t 中存放的 Class 信息是 Class 的地址,还是一个索引(根据该索引可在类信息表中查找该类结构地址)。可以看出,多了一个 uintptr_t indexcls : 15;。
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系统 支持 优化的isa指针。在 64 位环境下,优化的 isa 指针并不是就一定会存储引用计数,毕竟用 19bit (iOS 系统)保存引用计数不一定够。需要注意的是这 19 位保存的是引用计数的值减一。
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.mmSideTablestruct 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的定义为:
llvm-DenseMap.hDenseMap/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 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_setIvarWithStrongDefaultvoid object_setIvarWithStrongDefault(id obj, Ivar ivar, id value){ return _object_setIvar(obj, ivar, value, true /*strong default*/);}复制代码
objc-class.mm_object_setIvarstatic 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_storeStrongvoidobjc_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_setIvarvoid object_setIvar(id obj, Ivar ivar, id value){ return _object_setIvar(obj, ivar, value, false /*not strong default*/);}复制代码
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。读者可以从下面搜索一下这个关键词的位置。
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节中介绍的,不再赘述。
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();}复制代码可见,获取引用计数的关键在这么一句话:
objc-os.hLoadExclusive(uintptr_t *src)static ALWAYS_INLINEuintptr_t LoadExclusive(uintptr_t *src){ return *src;}复制代码
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:
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. 结论
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) | 黑马程序员IT技术论坛 X3.2 |