絕大多數(shù)iOS開(kāi)發(fā)者用過(guò)block
攻锰,并且知道用 __weak 的方式去解決循環(huán)引用的問(wèn)題。而進(jìn)階一些的開(kāi)發(fā)者則了解Weak-Strong-Dance
妓雾,那么什么是Weak-Strong-Dance
娶吞?它能保證block執(zhí)行是的“安全”嗎?
Weak-Strong-Dance
看看下面兩段代碼的區(qū)別械姻,你就明白什么是Weak-Strong-Dance
了妒蛇。
- (void)test {
__weak typeof(self) weakSelf = self;
self.block = ^{
[weakSelf copy];
};
}
- (void)test {
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(self) strongSelf = weakSelf;
[strongSelf copy];
};
}
也就是在用 __weak 解決循環(huán)引用的前提下 ,在block
內(nèi)部用 __strong 持有對(duì)象楷拳,試圖解決“在多線程下绣夺,可能weakSelf指向的對(duì)象會(huì)在 Block 執(zhí)行前被廢棄,導(dǎo)致各種各樣的問(wèn)題欢揖,比如說(shuō)KVO陶耍,傳入nil可是會(huì)crash呢”,如下代碼
__weak typeof(self) weakSelf = self;
self.handler = ^{
typeof(weakSelf) strongSelf = weakSelf;
[strongSelf.obserable removeObserver:strongSelf
forKeyPath:kObservableProperty];
};
此時(shí)她混,你可能會(huì)這樣認(rèn)為烈钞,self 所指向?qū)ο蟮囊糜?jì)數(shù)變成 2,即使主線程中的 self 因?yàn)槌鲎饔糜诙尫爬ぐ矗瑢?duì)象的引用計(jì)數(shù)依然為 1棵磷,避免了對(duì)象的銷(xiāo)毀。
思維糾正
它真的能解決在多線程下晋涣,可能 weakSelf 指向的對(duì)象會(huì)在 Block 執(zhí)行前被廢棄而導(dǎo)致的問(wèn)題嗎仪媒?
答案當(dāng)然是否定的,讓我們來(lái)看看demo:
不用Weak-Strong-Dance
:
#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
看看用clang改寫(xiě)后的代碼,這里就只貼關(guān)鍵代碼了:
// @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
代碼很長(zhǎng)算吩,解釋下:
在 struct __TestBlock__test_block_impl_0
里頭留凭,我們能看到TestBlock *const __weak weakSelf;
這代表在 block 內(nèi)部是以弱引用的方式捕獲 self 的,這沒(méi)毛病偎巢。重點(diǎn)來(lái)了蔼夜,看這一段代表 block 具體實(shí)現(xiàn)的代碼塊
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"));
}
這里可以看到如果此時(shí)外部廢棄了self,的確會(huì)導(dǎo)致 block 內(nèi)部訪問(wèn)成nil的情況压昼。
那么如果用了Weak-Strong-Dance
呢求冷?
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(self) strongSelf = weakSelf;
[strongSelf copy];
};
看看clang改寫(xiě)后會(huì)有什么區(qū)別:
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"));
}
holy shit!
區(qū)別在于在 block 內(nèi)多了這么一行代碼__attribute__((objc_ownership(strong))) typeof(self) strongSelf = weakSelf;
。
所以持有 self 的行為是在 block 執(zhí)行的時(shí)候才發(fā)生的窍霞!
回過(guò)頭來(lái)看看問(wèn)題:它真的能解決在多線程下匠题,可能 weakSelf 指向的對(duì)象會(huì)在 Block 執(zhí)行前被廢棄而導(dǎo)致的問(wèn)題嗎?
在執(zhí)行前就廢棄但金,到了執(zhí)行的時(shí)候韭山,weakSelf 已經(jīng)是 nil 了,此時(shí)執(zhí)行 __strong typeof(self) strongSelf = weakSelf;
根本沒(méi)意義吧冷溃。
所以在剛才KVO的例子中钱磅,該crash還是繼續(xù)crash吧。只要在執(zhí)行__strong typeof(self) strongSelf = weakSelf;
前似枕,對(duì)象在其他線程被廢棄了盖淡,Weak-Strong-Dance不能幫上任何忙!
總結(jié)
Weak-Strong-Dance
并不能保證 block所引用對(duì)象的釋放時(shí)機(jī)在執(zhí)行之后凿歼, 更安全的做法應(yīng)該是在 block 內(nèi)部使用 strongSelf 時(shí)進(jìn)行 nil檢測(cè)褪迟,這樣可以避免上述情況。