第一部分:借用 Xcode 找到與 weak 有關的全部內容,並進行整理和概括,推導出核心的數據結構和方法集。c++
第二部分:羅列和分析 weak 相關的重要數據結構,爲後面的方法集分析作準備。算法
第三部分:根據生命週期,從新羅列和分析 weak 的相關的方法集。數組
第四部分:分析第一部分留下的核心方法的實現,並作一個簡單的問答式總結。緩存
直接搜索 weak
,找到相關的可用內容bash
// 定義與聲明等內容
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 * _Nullable class_getWeakIvarLayout(Class _Nullable cls) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
OBJC_EXPORT void class_setWeakIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
OBJC_EXPORT id _Nullable objc_loadWeak(id _Nullable * _Nonnull location) OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
OBJC_EXPORT id _Nullable objc_storeWeak(id _Nullable * _Nonnull location, id _Nullable obj) OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
複製代碼
// 定義與聲明等內容
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 _Nullable objc_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 _Nullable objc_storeWeakOrNil(id _Nullable * _Nonnull location, id _Nullable obj) OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0, 2.0);
OBJC_EXPORT id _Nullable objc_initWeakOrNil(id _Nullable * _Nonnull location, id _Nullable val) OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0, 2.0);
OBJC_EXPORT void objc_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);
複製代碼
// 方法集
#if SUPPORT_NONPOINTER_ISA
inline bool
objc_object::isWeaklyReferenced()
{
assert(!isTaggedPointer());
if (isa.nonpointer) return isa.weakly_referenced;
else return sidetable_isWeaklyReferenced();
}
inline void
objc_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 bool
objc_object::isWeaklyReferenced()
{
assert(!isTaggedPointer());
return sidetable_isWeaklyReferenced();
}
inline void
objc_object::setWeaklyReferenced_nolock()
{
assert(!isTaggedPointer());
sidetable_setWeaklyReferenced_nolock();
}
#endif
複製代碼
// 定義與聲明等內容
/* 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.h
typedef 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);
複製代碼
#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)
#endif
struct 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;
// ...
};
複製代碼
// 定義與聲明等內容
// DisguisedPtr 在 objc-private.h 中聲明
typedef DisguisedPtr<objc_object *> weak_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 2
struct weak_entry_t {
DisguisedPtr<objc_object> 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);
複製代碼
// 定義與聲明等內容
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
// ...
};
複製代碼
template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating crashIfDeallocating>
static id storeWeak(id *location, objc_object *newObj);
複製代碼
// 方法集
// Privates
static 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);
// Publics
void 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 DEBUG
bool weak_is_registered_no_lock(weak_table_t *weak_table, id referent_id);
#endif
void weak_clear_no_lock(weak_table_t *weak_table, id referent_id);
複製代碼
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
複製代碼
objc_destroyWeak(...)
--> storeWeak(...) 💖
複製代碼
與 init Or destroy 類似的,莫過於 load Or store 了。數據結構
loadapp
objc_loadWeak(...)
--> objc_loadWeakRetained(...)
--> SEL_retainWeakReference
複製代碼
storeide
objc_storeWeak(...)
--> storeWeak(...) 💖
複製代碼
objc_storeWeakOrNil(...)
--> storeWeak(...) 💖
複製代碼
copy函數
objc_copyWeak(...)
--> objc_loadWeakRetained(...)
--> objc_initWeak(...)
複製代碼
move工具
objc_moveWeak(...)
--> objc_copyWeak(...)
--> objc_destroyWeak(...)
複製代碼
ivar
// class_ro_t->weakIvarLayout
const uint8_t * class_getWeakIvarLayout(Class cls) {
if (cls) return cls->data()->ro->weakIvarLayout;
else return nil;
}
void class_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
方法就是要研究的核心方法。
您可能還不能看懂它具體在幹嗎,經過2、三部分的分析,就能夠看清這個方法的全貌了。
// NSObject.mm
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;
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;
}
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));
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<haveOld, haveNew>(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
/* [封裝 nil ptr] * 這個類就是爲了讓 nil 指針像 non-nil 指針那樣『正常』運行它的操做,而不會讓程序崩掉。 */
// DisguisedPtr <T> 的做用相似指針類型 T*,除了
// 將存儲值隱藏起來,使其不受 「leaks」 之類工具的影響。
// nil 被假裝成它本身,這樣零填充的內存就像預期的那樣工做,
// 表示 0x80..00 也假裝成它本身,但咱們不在意。
// 注意 weak_entry_t 知道這種編碼。
template <typename T>
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<T>& ptr) : value(ptr.value) { }
// 運算符重載
// this 就是一個指向本身的指針
DisguisedPtr<T>& operator = (T* rhs) {
value = disguise(rhs);
return *this;
}
DisguisedPtr<T>& operator = (const DisguisedPtr<T>& 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
};
複製代碼
struct weak_entry_t {
// 保存 weak 對象的指針
DisguisedPtr<objc_object> referent;
union {
struct {
// typedef DisguisedPtr<objc_object *> 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
}
}
};
複製代碼
/** * 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
};
複製代碼
// 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<DisguisedPtr<objc_object>,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<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
複製代碼
補充:
DenseMap
就是一個散列表。(key-value)它使用的是平方探測法生成哈希值(key),還有一種就是線性探測(開放地址法)。
os_unfair_lock: 替代 OSSpinLock
/// 它的功能就是把自旋鎖的鎖操做從類中分離出來,並且類中必需要有一個自旋鎖屬性
/// StripMap 內部的實質是一個開放尋址法生成哈希鍵值的散列表 (雖然是寫着的 array ,可是是一個散列表)
enum { CacheLineSize = 64 };
// StripedMap<T> 是一個void* -> T 的映射,大小適當
// 用於緩存友好的鎖分離。
// 例如,這能夠用做 StripedMap<spinlock_t>
// 或者是 StripedMap<SomeStruct>,其中 SomeStruct 存儲一個自旋鎖。
template<typename T>
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<uintptr_t>(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
public:
// 操做符重載
// 便可以使用 StripMap<xxx>[objcPtr] 訪問
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}
const T& operator[] (const void *p) const {
return const_cast<StripedMap<T>>(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
的方法集。
本質上都是使用 class mutex_tt
封裝的 os_unfair_lock
自旋鎖。不難理解 ,就不細說了~~
template<>
void SideTable::lockTwo<DoHaveOld, DoHaveNew>
(SideTable *lock1, SideTable *lock2)
{
spinlock_t::lockTwo(&lock1->slock, &lock2->slock);
}
template<>
void SideTable::lockTwo<DoHaveOld, DontHaveNew>
(SideTable *lock1, SideTable *)
{
lock1->lock();
}
template<>
void SideTable::lockTwo<DontHaveOld, DoHaveNew>
(SideTable *, SideTable *lock2)
{
lock2->lock();
}
template<>
void SideTable::unlockTwo<DoHaveOld, DoHaveNew>
(SideTable *lock1, SideTable *lock2)
{
spinlock_t::unlockTwo(&lock1->slock, &lock2->slock);
}
template<>
void SideTable::unlockTwo<DoHaveOld, DontHaveNew>
(SideTable *lock1, SideTable *)
{
lock1->unlock();
}
template<>
void SideTable::unlockTwo<DontHaveOld, DoHaveNew>
(SideTable *, SideTable *lock2)
{
lock2->unlock();
}
複製代碼
💖 👏🏻 SideTable
裏面的 weak_table_t
💘 纔是真正的 weak
表,SideTable
是包含整個 MRC 內存管理的表結構。
// 咱們不能使用 c++ 靜態初始化器來初始化 SideTables,由於 libc 會在 c++ 初始化器運行以前調用咱們。咱們也不想要這個結構的全局指針,由於額外的間接。只能用困難的方法去作。
alignas(StripedMap<SideTable>) static uint8_t
SideTableBuf[sizeof(StripedMap<SideTable>)];
// Map: NSObject * (key) -- SideTable& (value)
static StripedMap<SideTable>& SideTables() {
return *reinterpret_cast<StripedMap<SideTable>*>(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
表示不擁有。
uintptr_t
objc_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;
}
複製代碼
id
objc_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;
}
複製代碼
bool
objc_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;
}
複製代碼
// rdar://20206767
// return uintptr_t instead of bool so that the various raw-isa
// -release paths all return zero in eax
uintptr_t
objc_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();
}
複製代碼
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;
}
複製代碼
注意:如下的兩個方法都是在平臺可使用位域的狀況下才會有效的。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();
}
複製代碼
// Used to assert that an object is not present in the side table.
bool
objc_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;
}
複製代碼
/** * 將 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;
}
}
複製代碼
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);
}
}
複製代碼
// 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
}
}
複製代碼
/** * 從弱引用表中刪除條目。 */
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);
}
複製代碼
/** * 返回給定被引用的弱引用表條目。若是沒有 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];
}
複製代碼
// 若是上面的方法都能看懂,那麼這個方法就很簡單了,我就不寫註釋了。
/** * 增加 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);
}
複製代碼
/** * 將給定 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++;
}
複製代碼
/** * 若是 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--;
}
複製代碼
/** * 註冊一個新的 (對象,弱指針) 對。若是它不存在就建立一個新的弱對象條目。 * * @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;
}
複製代碼
/** * 註銷一個已經存在的弱引用。 * 當銷燬引用的內存且引用還在的時候,就使用這個方法。(換句話說,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. */
void weak_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.
複製代碼
#if DEBUG
bool weak_is_registered_no_lock(weak_table_t *weak_table, id referent_id) {
return weak_entry_for_referent(weak_table, (objc_object *)referent_id);
}
#endif
複製代碼
/** * 被 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);
}
複製代碼
// NSObject.mm
template <HaveOld haveOld, HaveNew haveNew, CrashIfDeallocating crashIfDeallocating>
// 全局方法,靜態數據區
// 構建 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<haveOld, haveNew>(oldTable, newTable);
if (haveOld && *location != oldObj) {
// 若是有舊值,並且 *location 不是舊值的內存地址,則進行解鎖操做
SideTable::unlockTwo<haveOld, haveNew>(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<haveOld, haveNew>(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<haveOld, haveNew>(oldTable, newTable);
return (id)newObj;
}
複製代碼
一、弱引用對象保存在那?
StripMap<SideTable *>
中SideTable
中的 weak_table_t
中wea_entry_t
的 referent
成員中二、弱引用對象在何時會被自動置 nil ?
對象 dealloc
時會調用 weak_clear_no_lock
方法對錶中的弱引用對象進行置 nil
操做。
三、怎樣標記對象是一個弱引用對象的?
使用位域技術,保存在當前對象的引用計數值的二進制位中,標記位是第 0 位 (SIDE_TABLE_WEAKLY_REFERENCED) 。