引言:這篇文章旨在從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更多參考 這篇文章。數據結構
從#if defined(BUILD_FOR_OSX)
這句邏輯判斷可知, Object是針對macOS的,iOS開發暫時只關心NSObject便可。架構
Object.mm Objectapp
#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