其他分享
首页 > 其他分享> > 闲聊Objective-C中的weak

闲聊Objective-C中的weak

作者:互联网

前言

在objective-c中,weak几乎无处不在。尤其是定义ivar时,经常要用到这个关键字。用weak修饰的变量,在对象释放之后,对象会自动置为nil。它是怎么做到的呢?在研究它之前,有几个词有必要先了解一下。

关键词

SideTable

SideTable是一个结构体,主要有三个成员。它的作用就是用来管理对象的weak表,引用计数在此先不表。

struct SideTable {
    // 自旋锁
    spinlock_t slock;
    // 引用计数的hash表
    RefcountMap refcnts;
    // 存储对象弱引用指针的hash表
    weak_table_t weak_table;

    ...
}

其中weak表(weak_table)是实现weak功能的核心数据结构。其实也可以理解SideTable是对weak_table的一个封装,方便更好的使用和访问weak表。

weak表

weak表是一个hash表,存储了弱引用对象以及相关的所有弱引用的信息。key为对象的地址,value为指向该对象的weak指针地址数组。

/**
 * 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;
    size_t    num_entries;
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};

抽象一点的话可以像下面这么理解:

f(对象的地址) = 指向该对象的weak指针地址数组

weak_entry_t

weak_entries也是一个hash结构体,它存储的是指向绑定的弱引用对象的所有weak指针的地址。


/**
 * The internal structure stored in the weak references table. 
 * It maintains and stores
 * a hash set of weak references pointing to an object.
 * If out_of_line_ness != REFERRERS_OUT_OF_LINE then the set
 * is instead a small inline array.
 */
#define WEAK_INLINE_COUNT 4

// out_of_line_ness field overlaps with the low two bits of inline_referrers[1].
// inline_referrers[1] is a DisguisedPtr of a pointer-aligned address.
// The low two bits of a pointer-aligned DisguisedPtr will always be 0b00
// (disguised nil or 0x80..00) or 0b11 (any other address).
// Therefore out_of_line_ness == 0b10 is used to mark the out-of-line state.
#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];
        };
    };

    ...
};

weak的实现原理

释放

clearDeallocating中有两个分支,现实判断对象是否采用了优化isa引用计数,如果没有的话则需要清理对象存储在SideTable中的引用计数数据。如果对象采用了优化isa引用计数,则判断是都有使用SideTable的辅助引用计数(isa.has_sidetable_rc)或者有weak引用(isa.weakly_referenced),符合这两种情况中一种的,调用clearDeallocating_slow方法。

总结

weak功能的实现主要就是通过runtime维护一张hash表,在表里存储对象的地址和指向它的所有weak指针的地址。当存储对象被释放时,通过weak_clear_no_lock函数遍历存储weak指针地址的数组,把weak指针置空,从而避免野指针的问题。(因为对象一般分配在堆上的某块内存,如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉;而基础数据类型一般分配在栈上,栈的内存会由系统自己自动处理,不会造成野指针)

原文地址

标签:函数,对象,weak,地址,引用,Objective,闲聊,指针
来源: https://blog.51cto.com/u_15010671/2778152