Object Runtime -- Weak

零:导读

第一部分:借用 Xcode 找到与 weak 有关的所有内容,并进行整理和归纳,推导出核心的数据结构和方法集。

第二部分:罗列和分析 weak 相关的重要数据结构,为后面的方法集分析做准备。

第三部分:根据生命周期,重新罗列和分析 weak 的相关的方法集。

第四部分:分析第一部分留下的核心方法的实现,并做一个简单的问答式总结。

第一部分:Weak 的组成结构

找到 Weak 相关的内容

下载 Objc4 源码 :obj4,并用 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 * _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);

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 _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);

objc-object.h

// 方法集
#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

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.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);

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)

#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;
        // ...
};

objc-weak.h

// 定义与声明等内容

// 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);

NSObject.mm

// 定义与声明等内容

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);

objc-weak.mm

// 方法集

// 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);

以 初始化 -> 销毁 的方式查看源码,重新排列所有内容 [ 脱离文件 ]

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->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 方法就是要研究的核心方法。

storeWeak 方法的实现

您可能还不能看懂它具体在干嘛,通过二、三部分的分析,就可以看清这个方法的全貌了。

// 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_lockweak_unregister_no_lock
    • 它们的内部有两个重要的数据结构 weak_table_tweak_entry_t

第二部分:数据结构

StripMap -> SideTable —> weak_table_t —> weak_entry_t —> DisguisedPtr

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
};

weak_entry_t

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
        }
    }
};

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<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),还有一种就是线性探测(开放地址法)。

DenseMap

os_unfair_lock: 替代 OSSpinLock

Appe Synchronization

StripMap

/// 它的功能就是把自旋锁的锁操作从类中分离出来,而且类中必须要有一个自旋锁属性
/// 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 的方法集。

SideTable Methods

本质上都是使用 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();
}

objc_object::sideTable Methods

💖 👏🏻 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,也就是引用计数值真实值的起始位

首先我们知道,对象都是从 retainrelease 的,retain 表示拥有,release 表示不拥有。

Reference counting

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;
}

Retain

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;
}

Release

// 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();
}

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.
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;
}

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.
 */
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.

weak_is_registered_no_lock

#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

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.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;
}

总结

1、弱引用对象保存在那?

  • 程序中所有的弱引用对象保存在 StripMap<SideTable *>
  • 某个对象的弱引用对象保存在当前对象的 SideTable 中的 weak_table_t
  • 某个对象的单个弱引用对象保存在 wea_entry_treferent 成员中

2、弱引用对象在什么时候会被自动置 nil ?

对象 dealloc 时会调用 weak_clear_no_lock 方法对表中的弱引用对象进行置 nil 操作。

3、怎样标记对象是一个弱引用对象的?

使用位域技术,保存在当前对象的引用计数值的二进制位中,标记位是第 0 位 (SIDE_TABLE_WEAKLY_REFERENCED) 。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容