美文网首页
iOS weak指针

iOS weak指针

作者: Rockerliang | 来源:发表于2020-05-01 22:50 被阅读0次

weak 的用处

用一句话可归纳为:弱引用,在对象释放后置为 nil,避免错误的内存访问。用更通俗的话来表述是:weak 可以在不增加对象的引用计数的同时,又使得指针的访问是安全的。

SideTables, SideTable, weak_table

关系:

为了管理所有对象的引用计数和weak指针,苹果创建了一个全局的SideTables,虽然名字后面有个"s"不过他其实是一个全局的Hash表,里面的内容装的都是SideTable结构体而已。它使用对象的内存地址当它的key。管理引用计数和weak指针就靠它了。
一个obj,对应了一个SideTable。但是一个SideTable,会对应多个obj。因为SideTable的数量只有64个,所以会有很多obj共用同一个SideTable。

一个SideTable中,又有两个成员,分别是:

RefcountMap refcnts;        // 对象引用计数相关 map
weak_table_t weak_table;    // 对象弱引用相关 table

其中,refcents是一个hash map,其key是obj的地址,而value,则是obj对象的引用计数。

而weak_table则存储了弱引用obj的指针的地址,其本质是一个以obj地址为key,弱引用obj的指针的地址作为value的hash表。hash表的节点类型是weak_entry_t。

1.SideTables

先来说一下最外层的SideTables。SideTables可以理解为一个全局的hash数组,里面存储了SideTable类型的数据,其长度为64。

SideTables可以通过全局的静态函数获取:

static StripedMap<SideTable>& SideTables() {
    return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}

可以看到,SideTables 实质类型为模板类型StripedMap 。 StripedMap 是一个以void *为hash key, T为vaule的hash 表。hash定位的算法如下:

 static unsigned int indexForPointer(const void *p) { // 该方法以void *作为key 来获取void *对应在StripedMap 中的位置
        uintptr_t addr = reinterpret_cast<uintptr_t>(p);
        return ((addr >> 4) ^ (addr >> 9)) % StripeCount; // % StripeCount 防止index越界
    }
2. SideTable

SideTable定义如下:

struct SideTable {
    spinlock_t slock;           // 自旋锁,防止多线程访问冲突
    RefcountMap refcnts;        // 对象引用计数map
    weak_table_t weak_table;    // 对象弱引用map

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    // 锁操作 符合StripedMap对T的定义
    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);
};

苹果为SideTable还写了构造和析构函数:

// 构造函数
    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    //析构函数(看看函数体,苹果设计的SideTable其实不希望被析构,不然会引起fatal 错误)
    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

通过析构函数可以知道,SideTable是不能被析构的。

2.1 spinlock_t slock 自旋锁

它的作用是在操作引用计数的时候对SideTable加锁,避免数据错误。

自旋锁会忙等: 所谓忙等,即在访问被锁资源时,调用者线程不会休眠,而是不停循环在那里,直到被锁资源释放锁。
  互斥锁会休眠: 所谓休眠,即在访问被锁资源时,调用者线程会休眠,此时cpu可以调度其他线程工作。直到被锁资源释放锁。此时会唤醒休眠线程。

2.2 RefcountMap refcnts

RefcountMap refcnts 用来存储OC对象的引用计数。它实质上是一个以objc_object为key的hash表,其vaule就是OC对象的引用计数。同时,当OC对象的引用计数变为0时,会自动将相关的信息从hash表中剔除。

2.3 weak_table_t weak_table

用来存储OC对象弱引用的相关信息。我们知道,SideTables一共只有64个节点,而在我们的APP中,一般都会不只有64个对象,因此,多个对象一定会重用同一个SideTable节点,也就是说,一个weak_table会存储多个对象的弱引用信息。因此在一个SideTable中,又会通过weak_table作为hash表再次分散存储每一个对象的弱引用信息。
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;        // hash数组,用来存储弱引用对象的相关信息weak_entry_t
   size_t    num_entries;             // hash数组中的元素个数
   uintptr_t mask;                    // hash数组长度-1,会参与hash计算。(注意,这里是hash数组的长度,而不是元素个数。比如,数组长度可能是64,而元素个数仅存了2个)
   uintptr_t max_hash_displacement;   // 可能会发生的hash冲突的最大次数,用于判断是否出现了逻辑错误(hash表中的冲突次数绝不会超过改值)
};

其中weak_entry_t是存储在弱引用表中的一个内部结构体,它负责维护和存储指向一个对象的所有弱引用hash表。

weak 实现原理

Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。

weak 的实现原理可以概括一下三步:

1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。

3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

相关文章

  • iOS复习之strong、weak

    【IOS学习基础】weak和strong、懒加载、循环引用ARC指南1 - strong和weak指针

  • iOS weak指针

    weak 的用处 用一句话可归纳为:弱引用,在对象释放后置为 nil,避免错误的内存访问。用更通俗的话来表述是:w...

  • swift 中weak 和unowned的区别

    /* weak相当于OC里的weak iOS 5.0之后推出了weak.当我们创建的对象销毁了之后,指向的指针会指...

  • iOS面试题:weak修饰的释放则自动被置为nil的实现原理

    原文:iOS面试题大全 Runtime维护着一个Weak表,用于存储指向某个对象的所有Weak指针 Weak表是H...

  • NSMapTable

    NSMapTable: 不只是一个能放weak指针的 NSDictionary - isaced [iOS]NS...

  • iOS底层 (一) arc weak指针原理

    @[TOC](iOS arc weak指针原理) ARC 都帮我们做了什么? 是 LLVM 和 runTime 系...

  • OC--看objc源码认识weak

    weak修饰有什么用? 声明为weak的指针,weak指针指向的对象一旦被释放,weak的指针都将被赋值为nil ...

  • iOS - weak指针原理

    weak指针原理,即weak指针是怎么样在对象销毁的时候被置为nil的。这就要看runtime源码在对象销毁的时候...

  • 内存管理面试题

    1.weak实现原理当一个对象被weak指针指向时,这个weak指针会以对象为key存储到一个weak指针数组里面...

  • iOS中的weak指针

    ObjC runtime是如何实现weak指针的 用strong指针创建weak指针,系统会调用objc_init...

网友评论

      本文标题:iOS weak指针

      本文链接:https://www.haomeiwen.com/subject/zekivhtx.html