美文网首页
block 循环引用,__strong、__weak、__blo

block 循环引用,__strong、__weak、__blo

作者: 幸运者_Lucky | 来源:发表于2017-03-24 00:50 被阅读422次

    首先说一下循环引用, 为什么没用__weak 修饰就直接用 self.属性不会造成循环引用.
    循环引用是指两个或者多个对象循环持有造成的无法释放.
    Person 有个属性 block, 在 block 实现后, 此时 self 持有 block, 如果在 block 中, 直接使用 self, block 将持有 self, 造成循环引用, 如果 block 本身不是 self 的属性, 则 self不持有 block, 即使在 block 中直接使用 self 也不会造成循环引用, 但是为了避免多个对象的循环引用, 所以 block 中最好还是用__weak, 防止这种情况出现. 代理用 weak 与此同理.

    __block:
    使外部变量可以在 Block 内部进行修改.


    输出:
    num:2, pointer:0xb000000000000022
    blockNum:3, pointer:0xb000000000000032
    可见 __ block 会拷贝原来对象, __block修饰的对象可被 Block 内外同时修改.


    可见 doctorselfblock 持有, 即使没有使用__ block 修饰,但他们的属性依然可以被修改, 不会像上面的 num = @3; 那样报错禁止修改, 和 NSArray 是一样的, NSArray 本身是不可修改的, 但是 NSArray 里面存放的对象依然是可以修改的.

    __weak:



    weak的实现, 将当前对象(= 右边的对象)值的地址存入 weak hash 表, hash 表中的 keyweak 对象的地址, value 是当前对象(= 右边的对象)值的地址, 当每次访问weak 对象的时候, 首先找到 key, 再找到 value. 当当前对象的值被释放, weak hash 表将 value 设置为nil. 如果理解不了, 可以看一下 C语言的指针, 指针的指针.

    __weak 的作用: weak 表中的指向的对象, 不计入引用计数. 不持有对象, 但可以引用对象, 不参与对象释放.


    可见被__weak 修饰过的对象, 不会被 block 持有,所以在对象释放的时候, block 中的 weak 对象也被置为 nil.

    但有一种特殊的情况, 就是存放在栈区的对象并不会因为引用对象的释放而释放.


    字符串常量是存在栈区的, 栈内存并不会动态释放, 而是当当前线程执行完毕后, 释放当前线程的栈内存. 所有的常量都存在栈区.
    所以上面的例子中即使使用__ weak 修饰, 但是@"aa"这个常量并没有被释放, 所以 weak的地址指向依然存在值.

    __strong:
    在某个块内对当前对象引用计数加一.
    在 AFN 中经常出现 block 外面 __ weak, 在内部再 __strong.
    这里有个例子需要理解

    例 1

    在这个例子中, 在 block 里面已经通过__strong 对对象引用计数加一,但为什么置为 doctor 置为 nil, 就被释放掉了那?
    再看下面的例子:

    例 2

    这个例子为什么只有 doctor是空那?
    结合两个例子, 引用计数的改变是一个动态改变, 只有被强引用之后, 引用计数才会加一。

    分析例 1:

    doctor 置为 nil 之前, block__ strong 并没有执行, 所以当时 doctor 对象被当前的区块持有, 当 doctor 置为 nil 时, 该对象已经被释放, 所以 __strong 的时候, weak 地址的内存已经被释放, strong 指向 nil, 所以 doctor 对象引用计数并没有加 1.

    分析例 2:

    doctor 被置为nil之前执行__ strong, doctor 引用计数加 1, 对象被 block 持有, 执行 doctor = nil 之后, doctor 仍然被 block 持有.

    说一下外面__weak, 里面__strong的好处, 在被block引用的时候,不会被 block 持有, 在block执行完毕, 立即将对象释放, 并不会造成循环引用, 而且还可以在多线程中, 在 block 区块内对对象持有.

    这里有个概念就是区块持有和对象持有.

    {
         // 区块持有
        // 在块内声明的变量, 在块执行完毕立即被释放.
    }
    

    对象持有: 地址指向内存.

    相关文章

      网友评论

          本文标题:block 循环引用,__strong、__weak、__blo

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