iOS 底层解析weak的实现原理

参考地址---------:http://www.cocoachina.com/ios/20170328/18962.html

weak 实现原理的概括

Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址数组,为什么是指针的地址而不是直接是指针的数组?我认为只得到指针没法做后续更改指针的指向,比如置nil,或是内部算法,整改指向都受限。

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

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

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

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

下面将开始详细介绍每一步:

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

示例代码:

1

扫描二维码关注公众号,回复: 4782365 查看本文章

2

3

4

{

    NSObject *obj = [[NSObject alloc] init];

    id __weak obj1 = obj;

}

当我们初始化一个weak变量时,runtime会调用 NSObject.mm 中的objc_initWeak函数。这个函数在Clang中的声明如下:

1

id objc_initWeak(id *object, id value);

而对于 objc_initWeak() 方法的实现

1

2

3

4

5

6

7

8

9

10

11

12

id objc_initWeak(id *location, id newObj) {

// 查看对象实例是否有效

// 无效对象直接导致指针释放

    if (!newObj) {

        *location = nil;

        return nil;

    }

    // 这里传递了三个 bool 数值

    // 使用 template 进行常量参数传递是为了优化性能

    return storeWeakfalse/*old*/true/*new*/true/*crash*/>

    (location, (objc_object*)newObj);

}

可以看出,这个函数仅仅是一个深层函数的调用入口,而一般的入口函数中,都会做一些简单的判断(例如 objc_msgSend 中的缓存判断),这里判断了其指针指向的类对象是否有效,无效直接释放,不再往深层调用函数。否则,object将被注册为一个指向value的__weak对象。而这事应该是objc_storeWeak函数干的。

objc_initWeak、objc_storeWeak  大致做了如下图相关操作,更新维护两张表,还需要加锁保证多线程一致性,更新新旧哈希表。

QQ截图20170327155908.png

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

当weak引用指向的对象被释放时,又是如何去处理weak指针的呢?当释放对象时,其基本流程如下:

1、调用objc_release

2、因为对象的引用计数为0,所以执行dealloc

3、在dealloc中,调用了_objc_rootDealloc函数

4、在_objc_rootDealloc中,调用了object_dispose函数

5、调用objc_destructInstance

6、最后调用objc_clear_deallocating

重点看对象被释放时调用的objc_clear_deallocating函数。该函数实现如下:

1

2

3

4

5

6

7

void  objc_clear_deallocating(id obj) 

{

    assert(obj);

    assert(!UseGC);

    if (obj->isTaggedPointer()) return;

   obj->clearDeallocating();

}

也就是调用了clearDeallocating,继续追踪可以发现,它最终是使用了迭代器来取weak表的value,然后调用weak_clear_no_lock,然后查找对应的value,将该weak指针置空,weak_clear_no_lock函数的实现如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

/**

 * Called by dealloc; nils out all weak pointers that point to the

 * provided object so that they can no longer be used.

 *

 * @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;

    if (entry->out_of_line) {

        referrers = entry->referrers;

        count = TABLE_SIZE(entry);

    }

    else {

        referrers = entry->inline_referrers;

        count = WEAK_INLINE_COUNT;

    }

    for (size_t i = 0; i < count; ++i) {

        objc_object **referrer = referrers[i];

        if (referrer) {

            if (*referrer == referent) {

                *referrer = nil;

            }

            else if (*referrer) {

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

}

objc_clear_deallocating该函数的动作如下:

1、从weak表中获取废弃对象的地址为键值的记录

2、将包含在记录中的所有附有 weak修饰符变量的地址,赋值为nil

3、将weak表中该记录删除

4、从引用计数表中删除废弃对象的地址为键值的记录

看了objc-weak.mm的源码就明白了:其实Weak表是一个hash(哈希)表,然后里面的key是指向对象的地址,Value是Weak指针的地址的数组。

猜你喜欢

转载自blog.csdn.net/li198847/article/details/84452219