Weak-Strong-Dance真的安全吗?

mo38794891 7年前
   <p>绝大多数iOS开发者用过 block ,并且知道用 <strong>__weak</strong> 的方式去解决循环引用的问题。而进阶一些的开发者则了解 Weak-Strong-Dance ,那么什么是 Weak-Strong-Dance ?它能保证block执行是的 <strong>“安全”</strong> 吗?</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/374c4cf46c5edba64287550d91cf6ced.png"></p>    <h2>Weak-Strong-Dance</h2>    <p>看看下面两段代码的区别,你就明白什么是 Weak-Strong-Dance 了。</p>    <pre>  <code class="language-objectivec">- (void)test {      __weak typeof(self) weakSelf = self;      self.block = ^{          [weakSelf copy];      };  }  </code></pre>    <pre>  <code class="language-objectivec">- (void)test {      __weak typeof(self) weakSelf = self;      self.block = ^{          __strong typeof(self) strongSelf = weakSelf;          [strongSelf copy];      };  }  </code></pre>    <p>也就是在用 <strong>__weak</strong> 解决循环引用的前提下 ,在 block 内部用 <strong>__strong</strong> 持有对象,试图解决 “在多线程下,可能weakSelf指向的对象会在 Block 执行前被废弃,导致各种各样的问题,比如说KVO,传入nil可是会crash呢” ,如下代码</p>    <pre>  <code class="language-objectivec">__weak typeof(self) weakSelf = self;  self.handler = ^{      typeof(weakSelf) strongSelf = weakSelf;      [strongSelf.obserable removeObserver:strongSelf                                forKeyPath:kObservableProperty];  };  </code></pre>    <p>此时,你可能会这样认为, <strong>self</strong> 所指向对象的引用计数变成 2,即使主线程中的 self 因为超出作用于而释放,对象的引用计数依然为 1,避免了对象的销毁。</p>    <h2>思维纠正</h2>    <p>它真的能解决 在多线程下,可能 weakSelf 指向的对象会在 Block 执行前被废弃而导致的问题吗?</p>    <p>答案当然是 <strong>否定</strong> 的,让我们来看看demo:</p>    <p>不用 Weak-Strong-Dance :</p>    <pre>  <code class="language-objectivec">#import "TestBlock.h"    @interface TestBlock ()    @property (nonatomic, strong) dispatch_block_t block;    @end    @implementation TestBlock    - (void)test {      __weak typeof(self) weakSelf = self;      self.block = ^{          [weakSelf copy];      };  }    @end  </code></pre>    <p>看看用clang改写后的代码,这里就只贴关键代码了:</p>    <pre>  <code class="language-objectivec">// @interface TestBlock ()    // @property (nonatomic, strong) dispatch_block_t block;    /* @end */      // @implementation TestBlock        struct __TestBlock__test_block_impl_0 {    struct __block_impl impl;    struct __TestBlock__test_block_desc_0* Desc;    TestBlock *const __weak weakSelf;    __TestBlock__test_block_impl_0(void *fp, struct __TestBlock__test_block_desc_0 *desc, TestBlock *const __weak _weakSelf, int flags=0) : weakSelf(_weakSelf) {      impl.isa = &_NSConcreteStackBlock;      impl.Flags = flags;      impl.FuncPtr = fp;      Desc = desc;    }  };  static void __TestBlock__test_block_func_0(struct __TestBlock__test_block_impl_0 *__cself) {    TestBlock *const __weak weakSelf = __cself->weakSelf; // bound by copy            ((id (*)(id, SEL))(void *)objc_msgSend)((id)weakSelf, sel_registerName("copy"));      }  static void __TestBlock__test_block_copy_0(struct __TestBlock__test_block_impl_0*dst, struct __TestBlock__test_block_impl_0*src) {_Block_object_assign((void*)&dst->weakSelf, (void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);}    static void __TestBlock__test_block_dispose_0(struct __TestBlock__test_block_impl_0*src) {_Block_object_dispose((void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);}    static struct __TestBlock__test_block_desc_0 {    size_t reserved;    size_t Block_size;    void (*copy)(struct __TestBlock__test_block_impl_0*, struct __TestBlock__test_block_impl_0*);    void (*dispose)(struct __TestBlock__test_block_impl_0*);  } __TestBlock__test_block_desc_0_DATA = { 0, sizeof(struct __TestBlock__test_block_impl_0), __TestBlock__test_block_copy_0, __TestBlock__test_block_dispose_0};    static void _I_TestBlock_test(TestBlock * self, SEL _cmd) {      __attribute__((objc_ownership(weak))) typeof(self) weakSelf = self;      ((void (*)(id, SEL, dispatch_block_t))(void *)objc_msgSend)((id)self, sel_registerName("setBlock:"), ((void (*)())&__TestBlock__test_block_impl_0((void *)__TestBlock__test_block_func_0, &__TestBlock__test_block_desc_0_DATA, weakSelf, 570425344)));  }      static void(* _I_TestBlock_block(TestBlock * self, SEL _cmd) )(){ return (*(__strong dispatch_block_t *)((char *)self + OBJC_IVAR_$_TestBlock$_block)); }  static void _I_TestBlock_setBlock_(TestBlock * self, SEL _cmd, dispatch_block_t block) { (*(__strong dispatch_block_t *)((char *)self + OBJC_IVAR_$_TestBlock$_block)) = block; }  // @end  </code></pre>    <p>代码很长,解释下:</p>    <p>在 <strong>struct</strong> __TestBlock__test_block_impl_0 里头,我们能看到 TestBlock *const __weak weakSelf; 这代表在 <strong>block</strong> 内部是以 <strong>弱引用</strong> 的方式捕获 <strong>self</strong> 的,这没毛病。重点来了,看这一段代表 <strong>block</strong> 具体实现的代码块</p>    <pre>  <code class="language-objectivec">static void __TestBlock__test_block_func_0(struct __TestBlock__test_block_impl_0 *__cself) {    TestBlock *const __weak weakSelf = __cself->weakSelf; // bound by copy            ((id (*)(id, SEL))(void *)objc_msgSend)((id)weakSelf, sel_registerName("copy"));      }  </code></pre>    <p>这里可以看到如果此时外部废弃了 <strong>self</strong> ,的确会导致 <strong>block</strong> 内部访问成nil的情况。</p>    <p>那么如果用了 Weak-Strong-Dance 呢?</p>    <pre>  <code class="language-objectivec">__weak typeof(self) weakSelf = self;  self.block = ^{      __strong typeof(self) strongSelf = weakSelf;      [strongSelf copy];  };  </code></pre>    <p>看看clang改写后会有什么区别:</p>    <pre>  <code class="language-objectivec">  struct __TestBlock__test_block_impl_0 {    struct __block_impl impl;    struct __TestBlock__test_block_desc_0* Desc;    TestBlock *const __weak weakSelf;    __TestBlock__test_block_impl_0(void *fp, struct __TestBlock__test_block_desc_0 *desc, TestBlock *const __weak _weakSelf, int flags=0) : weakSelf(_weakSelf) {      impl.isa = &_NSConcreteStackBlock;      impl.Flags = flags;      impl.FuncPtr = fp;      Desc = desc;    }  };  static void __TestBlock__test_block_func_0(struct __TestBlock__test_block_impl_0 *__cself) {    TestBlock *const __weak weakSelf = __cself->weakSelf; // bound by copy            __attribute__((objc_ownership(strong))) typeof(self) strongSelf = weakSelf;          ((id (*)(id, SEL))(void *)objc_msgSend)((id)strongSelf, sel_registerName("copy"));      }  </code></pre>    <h2>holy shit!</h2>    <p>区别在于在 <strong>block</strong> 内多了这么一行代码 __attribute__((objc_ownership(strong))) typeof(self) strongSelf = weakSelf; 。</p>    <p>所以持有 <strong>self</strong> 的行为是在 <strong>block</strong> 执行的时候才发生的!</p>    <p>回过头来看看问题: 它真的能解决在多线程下,可能 weakSelf 指向的对象会在 Block 执行前被废弃而导致的问题吗?</p>    <p>在执行前就废弃,到了执行的时候, <strong>weakSelf</strong> 已经是 <strong>nil</strong> 了,此时执行 __strong typeof(self) strongSelf = weakSelf; 根本没意义吧。</p>    <p>所以在刚才KVO的例子中,该crash还是继续crash吧。只要在执行 __strong typeof(self) strongSelf = weakSelf; 前,对象在其他线程被废弃了,Weak-Strong-Dance不能帮上任何忙!</p>    <h2>总结</h2>    <p>Weak-Strong-Dance 并不能保证 <strong>block所引用对象的释放时机在执行之后</strong> , 更安全的做法应该是在 <strong>block</strong> 内部使用 <strong>strongSelf</strong> 时进行 <strong>nil检测</strong> ,这样可以避免上述情况。</p>    <p> </p>    <p>来自:http://kuailejim.com/2017/01/05/Weak-Strong-Dance真的安全吗?/</p>    <p> </p>