零:导读
第一部分:借用 Xcode 找到与 weak 有关的所有内容,并进行整理和归纳,推导出核心的数据结构和方法集。
第二部分:罗列和分析 weak 相关的重要数据结构,为后面的方法集分析做准备。
第三部分:根据生命周期,重新罗列和分析 weak 的相关的方法集。
第四部分:分析第一部分留下的核心方法的实现,并做一个简单的问答式总结。
第一部分:Weak 的组成结构
找到 Weak 相关的内容
下载 Objc4 源码 :,并用 Xcode 打开。[ 本文使用的是 objc4-750.1 ]
Xcode->Navigator->Show the find Navigator-> weak
直接搜索 weak
,找到相关的可用内容
runtime.h
// 定义与声明等内容typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { /**< Specifies a weak reference to the associated object. */ OBJC_ASSOCIATION_ASSIGN = 0, /**< */ OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, OBJC_ASSOCIATION_COPY_NONATOMIC = 3, OBJC_ASSOCIATION_RETAIN = 01401, OBJC_ASSOCIATION_COPY = 01403};复制代码
// 方法集OBJC_EXPORT const uint8_t * _Nullableclass_getWeakIvarLayout(Class _Nullable cls) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);OBJC_EXPORT voidclass_setWeakIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);OBJC_EXPORT id _Nullableobjc_loadWeak(id _Nullable * _Nonnull location) OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);OBJC_EXPORT id _Nullableobjc_storeWeak(id _Nullable * _Nonnull location, id _Nullable obj) OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);复制代码
objc-internal.h
// 定义与声明等内容typedef enum { objc_ivar_memoryUnknown, // unknown / unknown objc_ivar_memoryStrong, // direct access / objc_storeStrong objc_ivar_memoryWeak, // objc_loadWeak[Retained] / objc_storeWeak objc_ivar_memoryUnretained // direct access / direct access} objc_ivar_memory_management_t;复制代码
// 方法集OBJC_EXPORT id _Nullableobjc_loadWeakRetained(id _Nullable * _Nonnull location) OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);OBJC_EXPORT id _Nullable objc_initWeak(id _Nullable * _Nonnull location, id _Nullable val) OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);OBJC_EXPORT id _Nullableobjc_storeWeakOrNil(id _Nullable * _Nonnull location, id _Nullable obj) OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0, 2.0);OBJC_EXPORT id _Nullableobjc_initWeakOrNil(id _Nullable * _Nonnull location, id _Nullable val) OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0, 2.0);OBJC_EXPORT voidobjc_destroyWeak(id _Nullable * _Nonnull location) OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);OBJC_EXPORT void objc_copyWeak(id _Nullable * _Nonnull to, id _Nullable * _Nonnull from) OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);OBJC_EXPORT void objc_moveWeak(id _Nullable * _Nonnull to, id _Nullable * _Nonnull from) OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);复制代码
objc-object.h
// 方法集#if SUPPORT_NONPOINTER_ISAinline boolobjc_object::isWeaklyReferenced(){ assert(!isTaggedPointer()); if (isa.nonpointer) return isa.weakly_referenced; else return sidetable_isWeaklyReferenced();}inline voidobjc_object::setWeaklyReferenced_nolock(){ retry: isa_t oldisa = LoadExclusive(&isa.bits); isa_t newisa = oldisa; if (slowpath(!newisa.nonpointer)) { ClearExclusive(&isa.bits); sidetable_setWeaklyReferenced_nolock(); return; } if (newisa.weakly_referenced) { ClearExclusive(&isa.bits); return; } newisa.weakly_referenced = true; if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;}#else inline boolobjc_object::isWeaklyReferenced(){ assert(!isTaggedPointer()); return sidetable_isWeaklyReferenced();}inline void objc_object::setWeaklyReferenced_nolock(){ assert(!isTaggedPointer()); sidetable_setWeaklyReferenced_nolock();}#endif复制代码
objc-private.h
// 定义与声明等内容/* ISA_BITFIELD in isa.h */// xxx 是表示 arm64 / arm32 / x86_64 不同平台的值不一样# define ISA_BITFIELD \ uintptr_t nonpointer : xxx; \ uintptr_t has_assoc : xxx; \ uintptr_t has_cxx_dtor : xxx; \ uintptr_t shiftcls : xxx; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \ uintptr_t magic : xxx; \ uintptr_t weakly_referenced : xxx; \ uintptr_t deallocating : xxx; \ uintptr_t has_sidetable_rc : xxx; \ uintptr_t extra_rc : xxx/* isa */union isa_t { // ...#if defined(ISA_BITFIELD) struct { ISA_BITFIELD; // defined in isa.h };#endif};/* objc_object */struct objc_object { private: isa_t isa; // ...public: // ... // object may be weakly referenced? bool isWeaklyReferenced(); void setWeaklyReferenced_nolock(); // ...private: // ...#if SUPPORT_NONPOINTER_ISA // ... // 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(); // weak void sidetable_setWeaklyReferenced_nolock(); // weak 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};// layout.htypedef struct { uint8_t *bits; size_t bitCount; size_t bitsAllocated; bool weak;} layout_bitmap;复制代码
extern SEL SEL_retainWeakReference;extern SEL SEL_allowsWeakReference;extern bool noMissingWeakSuperclasses(void);复制代码
objc-runtime-new.h
#if !__LP64__// for class_rw_t->flags or class_t->bits// class or superclass has default retain/release/autorelease/retainCount/// _tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference# define RW_HAS_DEFAULT_RR (1<<14)// class or superclass has default retain/release/autorelease/retainCount/// _tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference# define FAST_HAS_DEFAULT_RR (1UL<<2)#else // class or superclass has default retain/release/autorelease/retainCount/// _tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference# define FAST_HAS_DEFAULT_RR (1UL<<49)#endifstruct class_rw_t { // Be warned that Symbolication knows the layout of this structure. uint32_t flags; // ... const class_ro_t *ro; // ...};// for class_ro_t->flags// class is not ARC but has ARC-style weak ivar layout #define RO_HAS_WEAK_WITHOUT_ARC (1<<9)struct class_ro_t { uint32_t flags; // ... const uint8_t * weakIvarLayout; // ...};复制代码
objc-weak.h
// 定义与声明等内容// DisguisedPtr 在 objc-private.h 中声明typedef DisguisedPtrweak_referrer_t;#if __LP64__# define PTR_MINUS_2 62#else# define PTR_MINUS_2 30#endif#define WEAK_INLINE_COUNT 4#define REFERRERS_OUT_OF_LINE 2struct weak_entry_t { DisguisedPtr referent; union { struct { weak_referrer_t *referrers; uintptr_t out_of_line_ness : 2; uintptr_t num_refs : PTR_MINUS_2; uintptr_t mask; uintptr_t max_hash_displacement; }; struct { // out_of_line_ness field is low bits of inline_referrers[1] weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; }; }; bool out_of_line() { return (out_of_line_ness == REFERRERS_OUT_OF_LINE); } weak_entry_t& operator=(const weak_entry_t& other) { memcpy(this, &other, sizeof(other)); return *this; } weak_entry_t(objc_object *newReferent, objc_object **newReferrer) : referent(newReferent) { inline_referrers[0] = newReferrer; for (int i = 1; i < WEAK_INLINE_COUNT; i++) { inline_referrers[i] = nil; } }};struct weak_table_t { weak_entry_t *weak_entries; size_t num_entries; uintptr_t mask; uintptr_t max_hash_displacement;};复制代码
// 方法集/// Adds an (object, weak pointer) pair to the weak table.id weak_register_no_lock(weak_table_t *weak_table, id referent, id *referrer, bool crashIfDeallocating);/// Removes an (object, weak pointer) pair from the weak table.void weak_unregister_no_lock(weak_table_t *weak_table, id referent, id *referrer);#if DEBUG/// Returns true if an object is weakly referenced somewhere.bool weak_is_registered_no_lock(weak_table_t *weak_table, id referent);#endif/// Called on object destruction. Sets all remaining weak pointers to nil.void weak_clear_no_lock(weak_table_t *weak_table, id referent);复制代码
NSObject.mm
// 定义与声明等内容struct SideTable { spinlock_t slock; RefcountMap refcnts; weak_table_t weak_table; // ... };复制代码
templatestatic id storeWeak(id *location, objc_object *newObj);复制代码
objc-weak.mm
// 方法集// Privatesstatic void grow_refs_and_insert(weak_entry_t *entry, objc_object **new_referrer);static void append_referrer(weak_entry_t *entry, objc_object **new_referrer);static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer);static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry);static void weak_resize(weak_table_t *weak_table, size_t new_size);static void weak_grow_maybe(weak_table_t *weak_table);static void weak_compact_maybe(weak_table_t *weak_table);static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry);static weak_entry_t *weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent);// Publicsvoid weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, id *referrer_id);id weak_register_no_lock(weak_table_t *weak_table, id referent_id, id *referrer_id, bool crashIfDeallocating);#if DEBUGbool weak_is_registered_no_lock(weak_table_t *weak_table, id referent_id);#endifvoid weak_clear_no_lock(weak_table_t *weak_table, id referent_id);复制代码
以 初始化 -> 销毁 的方式查看源码,重新排列所有内容 [ 脱离文件 ]
Step Start: init
objc_initWeak(...)objc_initWeakOrNil(...) --> storeWeak(...) ? --> weak_unregister_no_lock(...) ? --> weak_entry_for_referent(...) --> remove_referrer(...) --> weak_entry_remove(...) --> weak_register_no_lock(...) ? --> SEL_allowsWeakReference --> SEL_allowsWeakReference --> weak_entry_for_referent(...) --> append_referrer(...) --> weak_grow_maybe(...) --> weak_entry_insert(...) --> objc_object::setWeaklyReferenced_nolock(...) --> objc_object::sidetable_setWeaklyReferenced_nolock(...) --> weakly_referenced // ISA_BITFIELD复制代码
Step End: destroy
objc_destroyWeak(...) --> storeWeak(...) ?复制代码
Step Load Or Store: objc_xxxWeak
与 init Or destroy 相似的,莫过于 load Or store 了。
load
objc_loadWeak(...) --> objc_loadWeakRetained(...) --> SEL_retainWeakReference复制代码
store
objc_storeWeak(...) --> storeWeak(...) ?复制代码
objc_storeWeakOrNil(...) --> storeWeak(...) ?复制代码
Step Others: others
copy
objc_copyWeak(...) --> objc_loadWeakRetained(...) --> objc_initWeak(...)复制代码
move
objc_moveWeak(...) --> objc_copyWeak(...) --> objc_destroyWeak(...)复制代码
ivar
// class_ro_t->weakIvarLayoutconst uint8_t *class_getWeakIvarLayout(Class cls){ if (cls) return cls->data()->ro->weakIvarLayout; else return nil;}voidclass_setWeakIvarLayout(Class cls, const uint8_t *layout){ // ... class_ro_t *ro_w = make_ro_writeable(cls->data()); try_free(ro_w->weakIvarLayout); ro_w->weakIvarLayout = ustrdupMaybeNil(layout);}复制代码
结:很明显,storeWeak
方法就是要研究的核心方法。
storeWeak 方法的实现
您可能还不能看懂它具体在干嘛,通过二、三部分的分析,就可以看清这个方法的全貌了。
// NSObject.mmtemplatestatic id storeWeak(id *location, objc_object *newObj){ assert(haveOld || haveNew); if (!haveNew) assert(newObj == nil); Class previouslyInitializedClass = nil; id oldObj; SideTable *oldTable; SideTable *newTable; retry: if (haveOld) { oldObj = *location; oldTable = &SideTables()[oldObj]; } else { oldTable = nil; } if (haveNew) { newTable = &SideTables()[newObj]; } else { newTable = nil; } SideTable::lockTwo (oldTable, newTable); if (haveOld && *location != oldObj) { SideTable::unlockTwo (oldTable, newTable); goto retry; } if (haveNew && newObj) { Class cls = newObj->getIsa(); if (cls != previouslyInitializedClass && !((objc_class *)cls)->isInitialized()) { SideTable::unlockTwo (oldTable, newTable); _class_initialize(_class_getNonMetaClass(cls, (id)newObj)); previouslyInitializedClass = cls; goto retry; } } if (haveOld) { weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); } if (haveNew) { newObj = (objc_object *) weak_register_no_lock(&newTable->weak_table, (id)newObj, location, crashIfDeallocating); if (newObj && !newObj->isTaggedPointer()) { newObj->setWeaklyReferenced_nolock(); } *location = (id)newObj; } SideTable::unlockTwo (oldTable, newTable); return (id)newObj;}复制代码
- 里面有一个很重要的数据结构
SideTable
- 两个重要的方法:
weak_register_no_lock
、weak_unregister_no_lock
- 它们的内部有两个重要的数据结构
weak_table_t
、weak_entry_t
- 它们的内部有两个重要的数据结构
第二部分:数据结构
StripMap -> SideTable —> weak_table_t —> weak_entry_t —> DisguisedPtr
DisguisedPtr
/* [封装 nil ptr] * 这个类就是为了让 nil 指针像 non-nil 指针那样『正常』运行它的操作,而不会让程序崩掉。 */// DisguisedPtr的作用类似指针类型 T*,除了// 将存储值隐藏起来,使其不受 “leaks” 之类工具的影响。 // nil 被伪装成它自己,这样零填充的内存就像预期的那样工作,// 表示 0x80..00 也伪装成它自己,但我们不在乎。// 注意 weak_entry_t 知道这种编码。template class DisguisedPtr { // Disguise n. 伪装、假装、隐瞒 的意思 uintptr_t value; static uintptr_t disguise(T* ptr) { return -(uintptr_t)ptr; } static T* undisguise(uintptr_t val) { return (T*)-val; } public: DisguisedPtr() { } // :xxx, 初始化列表,显式初始化成员(编译器强制) // 等价于 this->value = disguise(ptr); DisguisedPtr(T* ptr) : value(disguise(ptr)) { } DisguisedPtr(const DisguisedPtr & ptr) : value(ptr.value) { } // 运算符重载 // this 就是一个指向自己的指针 DisguisedPtr & operator = (T* rhs) { value = disguise(rhs); return *this; } DisguisedPtr & operator = (const DisguisedPtr & rhs) { value = rhs.value; return *this; } operator T* () const { return undisguise(value); } T* operator -> () const { return undisguise(value); } T& operator * () const { return *undisguise(value); } T& operator [] (size_t i) const { return undisguise(value)[i]; } // pointer arithmetic operators omitted // because we don't currently use them anywhere};复制代码
weak_entry_t
struct weak_entry_t { // 保存 weak 对象的指针 DisguisedPtrreferent; union { struct { // typedef DisguisedPtr weak_referrer_t; // 保存 weak 对象指针的指针 weak_referrer_t *referrers; // C 语言的 位域(bitfield) 技术,可以节省空间 // out_of_line_ness + num_refs 刚好是一个 uintptr_t 的大小 // : 2 指变量 out_of_line_ness 只占 2 位(bit) uintptr_t out_of_line_ness : 2; // 标记是否越界 (即是否还能保存新的值) // 一共有多少个 弱引用 uintptr_t num_refs : PTR_MINUS_2; // PTR_MINUS_2 = __LP64__ ? 62 : 30; uintptr_t mask; // mask 就是数组的最大下标值,它主要是用来获取 index 的辅助值 (指针寻址),index 其实就是 Hash Key uintptr_t max_hash_displacement; // 记录已经放置了多少个 Hash Key }; struct { // 保存所有的 weak 对象指针 // out_of_line_ness field is low bits of inline_referrers[1] // WEAK_INLINE_COUNT = 4 weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; }; }; bool out_of_line() { // REFERRERS_OUT_OF_LINE = 2 return (out_of_line_ness == REFERRERS_OUT_OF_LINE); } // 运算符重载 weak_entry_t& operator=(const weak_entry_t& other) { // this 指向 other 的内存 memcpy(this, &other, sizeof(other)); // 内存拷贝 return *this; } // 构造器 weak_entry_t(objc_object *newReferent, objc_object **newReferrer) : referent(newReferent) { // 保存新的对象地址的地址,*newReferrer 就是 isa 的内存地址了 inline_referrers[0] = newReferrer; // 清空 out_of_line_ness,num_refs,mask 的内容 for (int i = 1; i < WEAK_INLINE_COUNT; i++) { // WEAK_INLINE_COUNT = 4 inline_referrers[i] = nil; // nil 就是 0 } }};复制代码
weak_table_t
/** * The global weak references table. Stores object ids as keys, * and weak_entry_t structs as their values. */struct weak_table_t { weak_entry_t *weak_entries; // 就是一个 weak_entry_t 数组 size_t num_entries; // weak_entry_t 的数量 uintptr_t mask; // mask 就是数组的最大下标值,它主要是用来获取 index 的辅助值 (指针寻址),index 其实就是 Hash Key uintptr_t max_hash_displacement; // 记录已经放置了多少个 Hash Key };复制代码
SideTable
// Template parameters.enum HaveOld { DontHaveOld = false, DoHaveOld = true };enum HaveNew { DontHaveNew = false, DoHaveNew = true };struct SideTable { // 内部封装的是 os_unfair_lock ,自旋锁 spinlock_t slock; // typedef objc::DenseMap,size_t,true> RefcountMap; // 对象引用计数的散列表 RefcountMap refcnts; // 弱引用表数据结构 weak_table_t weak_table; SideTable() { // 初始化:weak_table 内存清零,最后效果上 weak_table = nil; 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 static void lockTwo(SideTable *lock1, SideTable *lock2); template static void unlockTwo(SideTable *lock1, SideTable *lock2);};复制代码
补充:
DenseMap
就是一个散列表。(key-value)它使用的是平方探测法生成哈希值(key),还有一种就是线性探测(开放地址法)。
os_unfair_lock: 替代 OSSpinLock
StripMap
/// 它的功能就是把自旋锁的锁操作从类中分离出来,而且类中必须要有一个自旋锁属性/// StripMap 内部的实质是一个开放寻址法生成哈希键值的散列表 (虽然是写着的 array ,但是是一个散列表)enum { CacheLineSize = 64 };// StripedMap是一个void* -> T 的映射,大小适当// 用于缓存友好的锁分离。// 例如,这可以用作 StripedMap // 或者是 StripedMap ,其中 SomeStruct 存储一个自旋锁。template class StripedMap { #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR enum { StripeCount = 8 };#else enum { StripeCount = 64 };#endif struct PaddedT { T value alignas(CacheLineSize); }; // Hash 表数据 PaddedT array[StripeCount]; // Hash 键值生成函数 // 根据对象的内存地址计算,数组中的具体下标值 static unsigned int indexForPointer(const void *p) { uintptr_t addr = reinterpret_cast (p); return ((addr >> 4) ^ (addr >> 9)) % StripeCount; } public: // 操作符重载 // 即可以使用 StripMap [objcPtr] 访问 T& operator[] (const void *p) { return array[indexForPointer(p)].value; } const T& operator[] (const void *p) const { return const_cast >(this)[p]; } // 各种 for ,就是遍历操作元素的所有锁方法 // Shortcuts for StripedMaps of locks. void lockAll() { for (unsigned int i = 0; i < StripeCount; i++) { array[i].value.lock(); } } void unlockAll() { for (unsigned int i = 0; i < StripeCount; i++) { array[i].value.unlock(); } } void forceResetAll() { for (unsigned int i = 0; i < StripeCount; i++) { array[i].value.forceReset(); } } void defineLockOrder() { for (unsigned int i = 1; i < StripeCount; i++) { lockdebug_lock_precedes_lock(&array[i-1].value, &array[i].value); } } void precedeLock(const void *newlock) { // assumes defineLockOrder is also called lockdebug_lock_precedes_lock(&array[StripeCount-1].value, newlock); } void succeedLock(const void *oldlock) { // assumes defineLockOrder is also called lockdebug_lock_precedes_lock(oldlock, &array[0].value); } const void *getLock(int i) { if (i < StripeCount) return &array[i].value; else return nil; } #if DEBUG StripedMap() { // Verify alignment expectations. uintptr_t base = (uintptr_t)&array[0].value; uintptr_t delta = (uintptr_t)&array[1].value - base; assert(delta % CacheLineSize == 0); assert(base % CacheLineSize == 0); }#else constexpr StripedMap() {}#endif};复制代码
第三部分:算法 (操作集/方法集)
先查看 SideTable
方法集的实现,再查看 objc_object
使用 SideTable
的方法集。
SideTable Methods
本质上都是使用 class mutex_tt
封装的 os_unfair_lock
自旋锁。不难理解 ,就不细说了~~
template<>void SideTable::lockTwo(SideTable *lock1, SideTable *lock2){ spinlock_t::lockTwo(&lock1->slock, &lock2->slock);}template<>void SideTable::lockTwo (SideTable *lock1, SideTable *){ lock1->lock();}template<>void SideTable::lockTwo (SideTable *, SideTable *lock2){ lock2->lock();}template<>void SideTable::unlockTwo (SideTable *lock1, SideTable *lock2){ spinlock_t::unlockTwo(&lock1->slock, &lock2->slock);}template<>void SideTable::unlockTwo (SideTable *lock1, SideTable *){ lock1->unlock();}template<>void SideTable::unlockTwo (SideTable *, SideTable *lock2){ lock2->unlock();}复制代码
objc_object::sideTable Methods
? ?? SideTable
里面的 weak_table_t
? 才是真正的 weak
表,SideTable
是包含整个 MRC 内存管理的表结构。
// 我们不能使用 c++ 静态初始化器来初始化 SideTables,因为 libc 会在 c++ 初始化器运行之前调用我们。我们也不想要这个结构的全局指针,因为额外的间接。只能用困难的方法去做。alignas(StripedMap) static uint8_t SideTableBuf[sizeof(StripedMap )];// Map: NSObject * (key) -- SideTable& (value)static StripedMap & SideTables() { return *reinterpret_cast *>(SideTableBuf);}// CPU: Binary MSB LSB; // MSB = Most significant bit; 最高有效位// LSB = Least significant bit; 最低有效位// MSB-ward 最高有效位监控// 因为是使用位域的技术,所以标记位的位置当然很重要!!!// 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,监控 weak 的位#define SIDE_TABLE_RC_ONE (1UL<<2) // MSB-ward of deallocating bit,监控正在销毁的位// 这个位其实就是标记当前的引用计数值指针是否使用了位域技术的标记位。#define SIDE_TABLE_RC_PINNED (1UL<<(WORD_BITS-1)) // __LP64__ ? 64 : 32 ; 锁定位#define SIDE_TABLE_RC_SHIFT 2#define SIDE_TABLE_FLAG_MASK (SIDE_TABLE_RC_ONE-1) // 就是 3,也就是引用计数值真实值的起始位复制代码
首先我们知道,对象都是从 retain
到 release
的,retain
表示拥有,release
表示不拥有。
Reference counting
uintptr_tobjc_object::sidetable_retainCount(){ // 从全局 SideTable 中获取当前对象的 SideTable SideTable& table = SideTables()[this]; // 它的值是从 1 开始的, 因为 0 是要直接拿去销毁的~ size_t refcnt_result = 1; // 自旋锁 -- 锁 table.lock(); RefcountMap::iterator it = table.refcnts.find(this); // end() 和 second 是 C++ 的东西 // end() 指向 map (树) 的最后一节点, // second 是 pair 里面的 value, 就是要找的 SideTable 下对应的 引用计数值 if (it != table.refcnts.end()) { // #define SIDE_TABLE_RC_SHIFT 2 // this is valid for SIDE_TABLE_RC_PINNED too // 因为使用了位域技术,引用计数值前面的二进制位是: // SIDE_TABLE_WEAKLY_REFERENCED( 0 位),SIDE_TABLE_DEALLOCATING( 1 位) // SIDE_TABLE_RC_ONE( 2 位),刚好要移动 2 个位,进而取出正确的引用计数值 refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT; } // 自旋锁 -- 解锁 table.unlock(); return refcnt_result;}复制代码
注意:以下的几个方法都是在平台可以使用位域的情况下才会有效的。SUPPORT_NONPOINTER_ISA = YES 时生效。
// 将整个引用计数移到 SideTable,不管是正在释放的还是弱引用的都移过去。void objc_object::sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced){ assert(!isa.nonpointer); // should already be changed to raw pointer // 从全局 SideTable 中获取当前对象的 SideTable SideTable& table = SideTables()[this]; // 获取 引用计数值 (特殊存储的值,就是使用了位域技术处理的值) size_t& refcntStorage = table.refcnts[this]; size_t oldRefcnt = refcntStorage; // not deallocating - that was in the isa assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0); assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0); // 是否使用 位域 技术 uintptr_t carry; // addc (address carry) 基本猜测是 内存地址携带 功能,即 位域处理, // 这个方法就是用来保存引用计数值的,不过会按照是否使用位域技术进行保存罢了。 size_t refcnt = addc(oldRefcnt, extra_rc << SIDE_TABLE_RC_SHIFT, 0, &carry); // 锁定位,使用了位域技术 // SIDE_TABLE_RC_PINNED 是最后一位的前一位,这里的赋值就是初始化 引用计数值 的意思 if (carry) refcnt = SIDE_TABLE_RC_PINNED; if (isDeallocating) refcnt |= SIDE_TABLE_DEALLOCATING; // 置位 析构标记位 if (weaklyReferenced) refcnt |= SIDE_TABLE_WEAKLY_REFERENCED; // 置位 弱引用标记位 // 保存新的 引用计数值,size_t& 具备指针改值的功能哦! refcntStorage = refcnt;}复制代码
// 将一些引用计数从 isa 位域移到 SideTable。如果对象现在被锁定 (SIDE_TABLE_RC_PINNED == 1),则返回 true。// 首先 isa 也使用了位域技术,不过它被称为 Tagged Pointer 标记指针罢了。isa 里面也保存了很多信息。bool objc_object::sidetable_addExtraRC_nolock(size_t delta_rc){ assert(isa.nonpointer); // 从全局 SideTable 中获取当前对象的 SideTable SideTable& table = SideTables()[this]; // 获取 引用计数值 (特殊存储的值,就是使用了位域技术处理的值) size_t& refcntStorage = table.refcnts[this]; size_t oldRefcnt = refcntStorage; // isa-side bits should not be set here assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0); assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0); if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true; // 是否使用 位域 技术 uintptr_t carry; // addc (address carry) 基本猜测是 内存地址携带 功能,即 位域处理, // 这个方法就是用来保存引用计数值的,不过会按照是否使用位域技术进行保存罢了。 size_t newRefcnt = addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry); if (carry) { // 标记为位域技术处理后的引用计数值,并把真实的引用计数值保存吉祥指针中 refcntStorage = SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK); return true; } else { // 直接赋值,值本身就是引用计数值的真实值 refcntStorage = newRefcnt; return false; }}复制代码
// 将引用计数值从 SideTable 移动到 isa 位域中。返回减去的实际引用计数值,该值可能小于请求的值。size_t objc_object::sidetable_subExtraRC_nolock(size_t delta_rc){ assert(isa.nonpointer); // 从全局 SideTable 中获取当前对象的 SideTable SideTable& table = SideTables()[this]; // 没有引用计数值或者引用计数值为 0 (已经销毁了),直接返回 0 。 RefcountMap::iterator it = table.refcnts.find(this); if (it == table.refcnts.end() || it->second == 0) { // Side table retain count is zero. Can't borrow. return 0; } size_t oldRefcnt = it->second; // isa-side bits should not be set here assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0); assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0); // delta_rc 使用了位域,最高位是 1 size_t newRefcnt = oldRefcnt - (delta_rc << SIDE_TABLE_RC_SHIFT); assert(oldRefcnt > newRefcnt); // shouldn't underflow it->second = newRefcnt; return delta_rc;}复制代码
// 返回引用计数值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;}复制代码
Retain
idobjc_object::sidetable_retain(){#if SUPPORT_NONPOINTER_ISA assert(!isa.nonpointer);#endif // 从全局 SideTable 中获取当前对象的 SideTable SideTable& table = SideTables()[this]; // 自旋锁 - 锁 table.lock(); // 获取 引用计数值 (特殊存储的值,可能使用了位域技术处理的值) size_t& refcntStorage = table.refcnts[this]; // 如果是没有使用位域技术处理的引用计数值,直接加 4, // 对应于指针就是刚好是第三位,肯定是配合位域技术而做的特殊处理 if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) { refcntStorage += SIDE_TABLE_RC_ONE; } // 自旋锁 - 解锁 table.unlock(); return (id)this;}复制代码
boolobjc_object::sidetable_tryRetain(){#if SUPPORT_NONPOINTER_ISA assert(!isa.nonpointer);#endif // 从全局 SideTable 中获取当前对象的 SideTable 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()) { // 指引用计数值是 0 了,直接为 4 ,就是引用计数值重新变成『 1 』 table.refcnts[this] = SIDE_TABLE_RC_ONE; } else if (it->second & SIDE_TABLE_DEALLOCATING) { result = false; // 如果正在销毁,直接返回,表示不能被持有 } else if (! (it->second & SIDE_TABLE_RC_PINNED)) { // 如果是没有使用位域技术处理的引用计数值,直接减 4, // 对应于指针就是刚好是第三位,肯定是配合位域技术而做的特殊处理 it->second += SIDE_TABLE_RC_ONE; } return result;}复制代码
Release
// rdar://20206767// return uintptr_t instead of bool so that the various raw-isa // -release paths all return zero in eaxuintptr_tobjc_object::sidetable_release(bool performDealloc){#if SUPPORT_NONPOINTER_ISA assert(!isa.nonpointer);#endif // 从全局 SideTable 中获取当前对象的 SideTable SideTable& table = SideTables()[this]; bool do_dealloc = false; table.lock(); RefcountMap::iterator it = table.refcnts.find(this); if (it == table.refcnts.end()) { // 引用计数值为 0 , 标记为要销毁,同时标记引用计数值的正在销毁位 do_dealloc = true; // 直接赋值,就是把除正在销毁的其它 所有位置 0 了 。 table.refcnts[this] = SIDE_TABLE_DEALLOCATING; } else if (it->second < SIDE_TABLE_DEALLOCATING) { // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it. // 引用计数值为 0 , 标记为要销毁,同时标记引用计数值的正在销毁位 // 这里的置位,就是单纯的置位,没有破坏其它的标记位的值 do_dealloc = true; it->second |= SIDE_TABLE_DEALLOCATING; } else if (! (it->second & SIDE_TABLE_RC_PINNED)) { // 引用计数值减 『 1 』 it->second -= SIDE_TABLE_RC_ONE; } table.unlock(); if (do_dealloc && performDealloc) { // 向对象发送 dealloc 消息 ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc); } return do_dealloc;}复制代码
// 查看标记位,是否被标记为正在销毁bool objc_object::sidetable_isDeallocating(){ // 从全局 SideTable 中获取当前对象的 SideTable SideTable& table = SideTables()[this]; // NO SPINLOCK HERE // _objc_rootIsDeallocating() is called exclusively by _objc_storeWeak(), // 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 -_isDeallocating."); // } RefcountMap::iterator it = table.refcnts.find(this); return (it != table.refcnts.end()) && (it->second & SIDE_TABLE_DEALLOCATING);}复制代码
void objc_object::sidetable_clearDeallocating(){ // 从全局 SideTable 中获取当前对象的 SideTable SideTable& table = SideTables()[this]; // clear any weak table items // clear extra retain count and deallocating bit // (fixme warn or abort if extra retain count == 0 ?) table.lock(); RefcountMap::iterator it = table.refcnts.find(this); if (it != table.refcnts.end()) { if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) { // 如果引用计数值不为 0,而且标记为弱引用,则直接调弱引用的清空方法,清空当前对象的弱引用表 // weak_clear_no_lock 会在后面讲~~ weak_clear_no_lock(&table.weak_table, (id)this); } // 清空表内的所有元素 table.refcnts.erase(it); } table.unlock();}复制代码
Weak
bool objc_object::sidetable_isWeaklyReferenced(){ bool result = false; // 从全局 SideTable 中获取当前对象的 SideTable SideTable& table = SideTables()[this]; table.lock(); RefcountMap::iterator it = table.refcnts.find(this); if (it != table.refcnts.end()) { // 如果引用计数值不为 0,读取弱引用标记位 result = it->second & SIDE_TABLE_WEAKLY_REFERENCED; } table.unlock(); return result;}复制代码
void objc_object::sidetable_setWeaklyReferenced_nolock(){#if SUPPORT_NONPOINTER_ISA assert(!isa.nonpointer);#endif // 从全局 SideTable 中获取当前对象的 SideTable SideTable& table = SideTables()[this]; // 把当前对象标记为 弱引用 table.refcnts[this] |= SIDE_TABLE_WEAKLY_REFERENCED;}复制代码
Lock
注意:以下的两个方法都是在平台可以使用位域的情况下才会有效的。SUPPORT_NONPOINTER_ISA = YES 时生效。
// 为当前对象的 SideTable 表加锁void objc_object::sidetable_lock(){ SideTable& table = SideTables()[this]; table.lock();}// 为当前对象的 SideTable 表解锁void objc_object::sidetable_unlock(){ SideTable& table = SideTables()[this]; table.unlock();}复制代码
Debug
// Used to assert that an object is not present in the side table.boolobjc_object::sidetable_present(){ bool result = false; // 从全局 SideTable 中获取当前对象的 SideTable SideTable& table = SideTables()[this]; table.lock(); RefcountMap::iterator it = table.refcnts.find(this); if (it != table.refcnts.end()) result = true; // 向弱引用表注册当前对象 // weak_is_registered_no_lock 会在后面讲 if (weak_is_registered_no_lock(&table.weak_table, (id)this)) result = true; table.unlock(); return result;}复制代码
objc_weak.mm
weak_entry_insert
/** * 将 new_entry 添加到对象的弱引用表中。不再检查 referent 是否已经存在表中。 */static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry){ weak_entry_t *weak_entries = weak_table->weak_entries; assert(weak_entries != nil); // hash_pointer 就是一个 Hash Key 生成函数 size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask); size_t index = begin; size_t hash_displacement = 0; // 循环遍历整个弱引用数组,找到可以放置 new_entry 的 index 位置 (找空位) while (weak_entries[index].referent != nil) { index = (index+1) & weak_table->mask; if (index == begin) bad_weak_table(weak_entries); hash_displacement++; } // 保存新的弱引用条目 weak_entries[index] = *new_entry; weak_table->num_entries++; // 更新已经使用的 Hash Key 的数量 if (hash_displacement > weak_table->max_hash_displacement) { weak_table->max_hash_displacement = hash_displacement; }}复制代码
weak_resize
static void weak_resize(weak_table_t *weak_table, size_t new_size){ // #define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0) // 计算弱引用表的大小 size_t old_size = TABLE_SIZE(weak_table); weak_entry_t *old_entries = weak_table->weak_entries; // calloc : 分配内存且把内存空间的所有位置零 weak_entry_t *new_entries = (weak_entry_t *) calloc(new_size, sizeof(weak_entry_t)); // 重新初始化所有值 weak_table->mask = new_size - 1; weak_table->weak_entries = new_entries; weak_table->max_hash_displacement = 0; weak_table->num_entries = 0; // restored by weak_entry_insert below // 把旧条目插入到新的条目数组(哈希表)中 if (old_entries) { weak_entry_t *entry; weak_entry_t *end = old_entries + old_size; for (entry = old_entries; entry < end; entry++) { if (entry->referent) { weak_entry_insert(weak_table, entry); } } // 释放旧条目内存 free(old_entries); }}复制代码
weak_grow_maybe & weak_compact_maybe
// Grow the given zone's table of weak references if it is full.static void weak_grow_maybe(weak_table_t *weak_table){ // #define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0) // 计算弱引用表的大小 size_t old_size = TABLE_SIZE(weak_table); // 如果哈希表已经装满了 3/4 ,则让哈希表翻倍或分配 64 个哈希位(64 个指针位的宽度) // Grow if at least 3/4 full. if (weak_table->num_entries >= old_size * 3 / 4) { weak_resize(weak_table, old_size ? old_size*2 : 64); }}// Shrink the table if it is mostly empty.static void weak_compact_maybe(weak_table_t *weak_table){ // #define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0) // 计算弱引用表的大小 size_t old_size = TABLE_SIZE(weak_table); // 如果表内的槽大于 1024 个,而且占到了 1/16 表空间,则压缩表。 // Shrink if larger than 1024 buckets and at most 1/16 full. if (old_size >= 1024 && old_size / 16 >= weak_table->num_entries) { // 因为增长的时候是翻倍,所以压缩的时候就是一半咯! weak_resize(weak_table, old_size / 8); // leaves new table no more than 1/2 full }}复制代码
weak_entry_remove
/** * 从弱引用表中删除条目。 */static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry){ // remove entry // 如果 entry 的 referrers 已经被标记为 ness,则释放条目的内容 if (entry->out_of_line()) free(entry->referrers); // 二进制位置零 bzero(entry, sizeof(*entry)); weak_table->num_entries--; // 压缩表 weak_compact_maybe(weak_table);}复制代码
weak_entry_for_referent
/** * 返回给定被引用的弱引用表条目。如果没有 referent 对应的条目,返回 NULL 。相当于执行一个查询操作。 * * @param weak_table * @param referent The object. Must not be nil. * * @return The table of weak referrers to this object. */static weak_entry_t *weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent){ assert(referent); weak_entry_t *weak_entries = weak_table->weak_entries; if (!weak_entries) return nil; // 得到 Hash Key size_t begin = hash_pointer(referent) & weak_table->mask; size_t index = begin; size_t hash_displacement = 0; // 循环遍历 Hash 表 while (weak_table->weak_entries[index].referent != referent) { index = (index+1) & weak_table->mask; if (index == begin) bad_weak_table(weak_table->weak_entries); hash_displacement++; // 最大的哈希放置值 : 即已经在表中放置了多少个条目 // 如果发现遍历过程中,hash_displacement 大于最大的哈希放置值,则退出循环 if (hash_displacement > weak_table->max_hash_displacement) { return nil; } } return &weak_table->weak_entries[index];}复制代码
grow_refs_and_insert
// 如果上面的方法都能看懂,那么这个方法就很简单了,我就不写注释了。/** * 增长 referrer 的哈希表。重哈希每个 referrer。 * * @param entry Weak pointer hash set for a particular object. */__attribute__((noinline, used))static void grow_refs_and_insert(weak_entry_t *entry, objc_object **new_referrer){ assert(entry->out_of_line()); size_t old_size = TABLE_SIZE(entry); size_t new_size = old_size ? old_size * 2 : 8; size_t num_refs = entry->num_refs; weak_referrer_t *old_refs = entry->referrers; entry->mask = new_size - 1; entry->referrers = (weak_referrer_t *) calloc(TABLE_SIZE(entry), sizeof(weak_referrer_t)); entry->num_refs = 0; entry->max_hash_displacement = 0; for (size_t i = 0; i < old_size && num_refs > 0; i++) { if (old_refs[i] != nil) { append_referrer(entry, old_refs[i]); num_refs--; } } // Insert append_referrer(entry, new_referrer); if (old_refs) free(old_refs);}复制代码
append_referrer
/** * 将给定 referrer 添加到此条目中的弱指针集。不执行重复检查 (b/c 弱指针不会添加两次到集合中 )。 * * @param entry The entry holding the set of weak pointers. * @param new_referrer The new weak pointer to be added. */static void append_referrer(weak_entry_t *entry, objc_object **new_referrer){ if (! entry->out_of_line()) { // 尝试插入到 entry 的 inline_referrers 中 // Try to insert inline. for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) { if (entry->inline_referrers[i] == nil) { entry->inline_referrers[i] = new_referrer; return; } } // 如果无法插入,则构建一个小型的数组保存 new_referrer // Couldn't insert inline. Allocate out of line. weak_referrer_t *new_referrers = (weak_referrer_t *) calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t)); // This constructed table is invalid, but grow_refs_and_insert // will fix it and rehash it. // #define WEAK_INLINE_COUNT = 4 for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) { new_referrers[i] = entry->inline_referrers[i]; } entry->referrers = new_referrers; entry->num_refs = WEAK_INLINE_COUNT; entry->out_of_line_ness = REFERRERS_OUT_OF_LINE; // 越界了,没法保存新的值了 entry->mask = WEAK_INLINE_COUNT-1; // 3 entry->max_hash_displacement = 0; } assert(entry->out_of_line()); // 重构整个表 // 如果真的 out_of_line 了,那么 TABLE_SIZE(entry) = mask + 1 = 4,而且 num_refs = 4, // 即触发重构表的操作 grow_refs_and_insert ,增长表且重新插入所有条目 if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) { return grow_refs_and_insert(entry, new_referrer); } // 下面的操作非常熟了吧~~~ size_t begin = w_hash_pointer(new_referrer) & (entry->mask); size_t index = begin; size_t hash_displacement = 0; while (entry->referrers[index] != nil) { hash_displacement++; index = (index+1) & entry->mask; if (index == begin) bad_weak_table(entry); } if (hash_displacement > entry->max_hash_displacement) { entry->max_hash_displacement = hash_displacement; } weak_referrer_t &ref = entry->referrers[index]; ref = new_referrer; entry->num_refs++;}复制代码
remove_referrer
/** * 如果 old_referrer 存在表中,则从 referrers 中删除 old_referrer 。 * 不会触发删除两次的操作,因为表中不会存在两个相同的 old_referrer。 * * @todo this is slow if old_referrer is not present. Is this ever the case? * * @param entry The entry holding the referrers. * @param old_referrer The referrer to remove. */static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer){ if (! entry->out_of_line()) { // 这里就是那个 @todo 说的导致慢的原因,循环遍历嘛。 // 查找表中是否有 old_referrer for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) { if (entry->inline_referrers[i] == old_referrer) { entry->inline_referrers[i] = nil; return; } } _objc_inform("Attempted to unregister unknown __weak variable " "at %p. This is probably incorrect use of " "objc_storeWeak() and objc_loadWeak(). " "Break on objc_weak_error to debug.\n", old_referrer); objc_weak_error(); return; } // 下面的操作非常熟了吧~~~ size_t begin = w_hash_pointer(old_referrer) & (entry->mask); size_t index = begin; size_t hash_displacement = 0; while (entry->referrers[index] != old_referrer) { index = (index+1) & entry->mask; if (index == begin) bad_weak_table(entry); hash_displacement++; if (hash_displacement > entry->max_hash_displacement) { _objc_inform("Attempted to unregister unknown __weak variable " "at %p. This is probably incorrect use of " "objc_storeWeak() and objc_loadWeak(). " "Break on objc_weak_error to debug.\n", old_referrer); objc_weak_error(); return; } } entry->referrers[index] = nil; entry->num_refs--;}复制代码
weak_register_no_lock
/** * 注册一个新的 (对象,弱指针) 对。如果它不存在就创建一个新的弱对象条目。 * * @param weak_table 全局的弱引用表 * @param referent 被弱引用的对象 * @param referrer 被弱引用的对象的内存地址 */id weak_register_no_lock(weak_table_t *weak_table, id referent_id, id *referrer_id, bool crashIfDeallocating){ objc_object *referent = (objc_object *)referent_id; objc_object **referrer = (objc_object **)referrer_id; // 对象指针为 nil,直接返回 // 或者 // 对象的 isa 没有使用 Tagged Pointer (位域 技术),也返回。 if (!referent || referent->isTaggedPointer()) return referent_id; // ensure that the referenced object is viable bool deallocating; // 通过对象地址找到对应的类 (RR: retain/release) // hasCustomRR: 是否有自定义实现 retain/release 方法 if (!referent->ISA()->hasCustomRR()) { // 如果没有自定义释放方法,则通过 rootIsDeallocating 得到对象是否正在销毁 deallocating = referent->rootIsDeallocating(); } else { // typedef 是我专门加的,方便查看代码~ typedef BOOL (*AllowsWeakReference)(objc_object *, SEL); // 获取 allowsWeakReference 方法的函数地址 AllowsWeakReference allowsWeakReference = (AllowsWeakReference) object_getMethodImplementation((id)referent, SEL_allowsWeakReference); // 如果没找到,直接返回 nil if ((IMP)allowsWeakReference == _objc_msgForward) { return nil; } // 调用 allowsWeakReference 方法得到对象是否正在销毁的值 deallocating = ! (*allowsWeakReference)(referent, SEL_allowsWeakReference); } // 如果对象正在销毁 if (deallocating) { // 如果需要让程序 Crash if (crashIfDeallocating) { _objc_fatal("Cannot form weak reference to instance (%p) of " "class %s. It is possible that this object was " "over-released, or is in the process of deallocation.", (void*)referent, object_getClassName((id)referent)); } else { return nil; } } // now remember it and where it is being stored weak_entry_t *entry; // 从弱引用表中查找当前对象的弱引用条目,如果能够找到(已经添加过弱引用了),则 // 通过 append_referrer 尝试插入新的弱引用条目,并更新条目内容 if ((entry = weak_entry_for_referent(weak_table, referent))) { append_referrer(entry, referrer); } else { // 如果没有,则构建新的条目 weak_entry_t new_entry(referent, referrer); // 检查是否需要增长表 weak_grow_maybe(weak_table); // 插入新的条目 weak_entry_insert(weak_table, &new_entry); } // Do not set *referrer. objc_storeWeak() requires that the // value not change. return referent_id;}复制代码
weak_unregister_no_lock
/** * 注销一个已经存在的弱引用。 * 当销毁引用的内存且引用还在的时候,就使用这个方法。(换句话说,0 引用随后会变成一个坏内存访问) * * Does nothing if referent/referrer is not a currently active weak reference. * Does not zero referrer. * * FIXME currently requires old referent value to be passed in (lame) * FIXME unregistration should be automatic if referrer is collected * * @param weak_table The global weak table. * @param referent The object. * @param referrer The weak reference. */voidweak_unregister_no_lock(weak_table_t *weak_table, id referent_id, id *referrer_id){ objc_object *referent = (objc_object *)referent_id; objc_object **referrer = (objc_object **)referrer_id; weak_entry_t *entry; if (!referent) return; if ((entry = weak_entry_for_referent(weak_table, referent))) { // 移除条目中的弱引用 remove_referrer(entry, referrer); // 标记是否需要从表中移除 entry bool empty = true; if (entry->out_of_line() && entry->num_refs != 0) { empty = false; } else { for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) { if (entry->inline_referrers[i]) { empty = false; break; } } } if (empty) { // 移除表中的弱引用条目 weak_entry_remove(weak_table, entry); } } // Do not set *referrer = nil. objc_storeWeak() requires that the // value not change.复制代码
weak_is_registered_no_lock
#if DEBUGboolweak_is_registered_no_lock(weak_table_t *weak_table, id referent_id) { return weak_entry_for_referent(weak_table, (objc_object *)referent_id);}#endif复制代码
weak_clear_no_lock
/** * 被 dealloc 调用;把所有提供的对象的弱引用指针置 nil,这样它们将不会再被使用了。 * * @param weak_table 弱引用表 * @param referent The object being deallocated. 需要销毁对象 */void weak_clear_no_lock(weak_table_t *weak_table, id referent_id) { objc_object *referent = (objc_object *)referent_id; // 从弱引用表中获取弱引用条目 weak_entry_t *entry = weak_entry_for_referent(weak_table, referent); if (entry == nil) { /// XXX shouldn't happen, but does with mismatched CF/objc //printf("XXX no entry for clear deallocating %p\n", referent); return; } // zero out references weak_referrer_t *referrers; size_t count; // 如果您还记得 weak_entry_t 的数据结构应该很容易明白,这里会出现两种情况 // 第一种,是越界的处理方式,第二种是没有越界的处理方式 if (entry->out_of_line()) { referrers = entry->referrers; count = TABLE_SIZE(entry); // entry->mask + 1,数组的大小 } else { // mini Array referrers = entry->inline_referrers; count = WEAK_INLINE_COUNT; // 4 } for (size_t i = 0; i < count; ++i) { objc_object **referrer = referrers[i]; if (referrer) { // 如果有弱引用值,则置 nil if (*referrer == referent) { *referrer = nil; } else if (*referrer) { // 下面的信息是说明,如果没有找到弱引用值(二级指针),则是错误地使用了 // objc_storeWeak() and objc_loadWeak() 方法导致的结果。 _objc_inform("__weak variable at %p holds %p instead of %p. " "This is probably incorrect use of " "objc_storeWeak() and objc_loadWeak(). " "Break on objc_weak_error to debug.\n", referrer, (void*)*referrer, (void*)referent); objc_weak_error(); } } } // 从弱引用表中移除弱引用条目 weak_entry_remove(weak_table, entry);}复制代码
第四部分:storeWeak & 总结
storeWeak 方法
// NSObject.mmtemplate// 全局方法,静态数据区// 构建 weak 引用结构static id storeWeak(id *location, objc_object *newObj /* 新值 */){ assert(haveOld || haveNew); if (!haveNew) assert(newObj == nil); Class previouslyInitializedClass = nil; // 旧值 id oldObj; // 内存管理表 (不是单纯的 weak 表哦!) SideTable *oldTable; SideTable *newTable; // 为旧值和新值获取锁。 // 按地址获取锁,防止获取锁出现问题。 // 如果旧值在我们底部发生了变化,请重试。 retry: // 如果对象有旧值,就从内存管理表中获取旧值,否则直接置 nil if (haveOld) { oldObj = *location; oldTable = &SideTables()[oldObj]; } else { oldTable = nil; } // 如果有新值,就从内存管理表中获取新值,否则置 nil if (haveNew) { newTable = &SideTables()[newObj]; } else { newTable = nil; } // lockTwo 是根据谁有值就调谁的锁,触发加锁 ( C++ 方法重载),如果两个都有值,那么两个都加锁咯! SideTable::lockTwo (oldTable, newTable); if (haveOld && *location != oldObj) { // 如果有旧值,而且 *location 不是旧值的内存地址,则进行解锁操作 SideTable::unlockTwo (oldTable, newTable); // 回去 retry 标签处,再来一次,反正一直到正确为止,这里才会跳过 (就是一个循环) goto retry; } // 通过确保没有弱引用对象是否有『 -/+initialized 』的 isa 指针, // 来防止弱引用机制和『 +initialized 』机制之间发生死锁。 if (haveNew && newObj) { // 获取对象的 isa 指针,因为 isa 指针是 objc_class 类的第一个成员, // 即 isa 指针的地址值就是 Class 的地址值。 Class cls = newObj->getIsa(); // 因为这里有循环结构 goto retry ,所以会出现前一个初始化的类这个概念 // 如果不是已经初始化的类,则加锁并初始化类 if (cls != previouslyInitializedClass && !((objc_class *)cls)->isInitialized()) { // 加锁 SideTable::unlockTwo (oldTable, newTable); // 调用对象所在类的(不是元类)初始化方法, // 即 调用的是 [newObjClass initialize]; 类方法 _class_initialize(_class_getNonMetaClass(cls, (id)newObj)); // 如果这个类在这个线程中完成了 +initialize 的任务,那么这很好。 // 如果这个类还在这个线程中继续执行着 +initialize 任务, // (比如,这个类的实例在调用 storeWeak 方法,而 storeWeak 方法调用了 +initialize .) // 这样我们可以继续运行,但在上面它将进行初始化和尚未初始化的检查。 // 相反,在重试时设置 previouslyInitializedClass 为这个类来识别它。 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 (oldTable, newTable); return (id)newObj;}复制代码
总结
1、弱引用对象保存在那?
- 程序中所有的弱引用对象保存在
StripMap<SideTable *>
中 - 某个对象的弱引用对象保存在当前对象的
SideTable
中的weak_table_t
中 - 某个对象的单个弱引用对象保存在
wea_entry_t
的referent
成员中
2、弱引用对象在什么时候会被自动置 nil ?
对象 dealloc
时会调用 weak_clear_no_lock
方法对表中的弱引用对象进行置 nil
操作。
3、怎样标记对象是一个弱引用对象的?
使用位域技术,保存在当前对象的引用计数值的二进制位中,标记位是第 0 位 (SIDE_TABLE_WEAKLY_REFERENCED) 。