??現(xiàn)在block是越來越多人在用晓锻,幾乎代替了delegate专执,你可以發(fā)現(xiàn)現(xiàn)在apple新出的API奈偏,以及升級的API坞嘀,都是block去實現(xiàn)的,這里優(yōu)缺點(diǎn)不說了惊来,只說其中一個最重要的注意點(diǎn):循環(huán)引用丽涩。
??block+循環(huán)引用,這里我們將這個問題拆開來,逐一解決矢渊,從此媽媽再也不擔(dān)心我使用block了继准。
block
??最簡單的解釋:代碼塊(內(nèi)聯(lián)函數(shù)),可以內(nèi)嵌在方法體中矮男。
??上面的字面解釋移必,相信是大多數(shù)初學(xué)者的認(rèn)知。下面是我個人的見解毡鉴,希望可以幫到你崔泵,上代碼
??先看一個最簡單的block,沒有用外部對象self的猪瞬,如下:
typedef void(^Ice)(NSString *name);
@implementation IceCream
- (instancetype)init {
if (self = [super init]) {
NSString *string = @"123";
Ice iceBlock = ^(NSString *name) {
NSLog(@"%@",name);
};
iceBlock(string);
}
return self;
}
@end
??這里我們看到的是OC代碼憎瘸,簡單的聲明了一個局部變量iceBlock,光從OC角度來看撑螺,并不能看出來什么含思,那么我們往深了看,讓我們看一下clang編譯器將他變成c++的樣子甘晤,看看最后編譯器將他解釋成一個什么含潘。(下面只是截取我認(rèn)為有用的部分代碼)
struct __block_impl {
void *isa;//相信知道OC對象的,應(yīng)該也知道isa指針吧线婚,原來block也是可以看做對象的
int Flags;//
int Reserved;
void *FuncPtr;//方法體
};
struct __IceCream__init_block_impl_0 {
struct __block_impl impl;
struct __IceCream__init_block_desc_0* Desc;
__IceCream__init_block_impl_0(void *fp, struct __IceCream__init_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
??這里我們看到兩個結(jié)構(gòu)體遏弱,一個__block_impl
(block最最基本的機(jī)構(gòu)),一個__IceCream__init_block_impl_0
塞弊。
??第二個結(jié)構(gòu)體里面有一個指針impl
指向了上面的結(jié)構(gòu)體漱逸,也就是block最基本的內(nèi)容,至于這個結(jié)構(gòu)體名字為什么叫__IceCream__init_block_impl_0
游沿,應(yīng)該是指iceBlock
外面的init
方法(個人猜測)饰抒,這個結(jié)構(gòu)體里面的__IceCream__init_block_impl_0
方法,并且有入?yún)⒕魇颍椒ɡ锩媸菍?code>__block_impl的賦值袋坑,以及對其他指針的賦值(這里看到的是__IceCream__init_block_desc_0* Desc
)其實是對自身的一個解釋賦值,里面有flags
眯勾,desc
枣宫,最重要的是isa
和FuncPtr
。(個人建議:如果你把C++的結(jié)構(gòu)體看做OC的對象吃环,可能會更好理解一點(diǎn))這只是簡單的一個局部變量也颤,并且里面是沒有用到外部任何變量的情況。
??下面我們看一個引用了self外部對象參數(shù)的block
typedef void(^Person)(void);
@interface ViewController ()
@property (nonatomic, copy) Person personBlock;
@property (nonatomic, strong) NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.name = @"VenpleD";
self.personBlock = ^{
self.name = @"HelloWorld";
};
}
再來將他編譯成c++文件
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
ViewController *self;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, ViewController *_self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
/***block方法實現(xiàn)的imp*****/
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
ViewController *self = __cself->self; // bound by copy
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_mm_hb3kj211641625fw0pq0xlkrkk1cf__T_ViewController_1108c8_mi_1);
}
/***viewdidload***/
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_mm_hb3kj211641625fw0pq0xlkrkk1cf__T_ViewController_1108c8_mi_0);
/**調(diào)用personBlock**/
((void (*)(id, SEL, Person))(void *)objc_msgSend)((id)self, sel_registerName("setPersonBlock:"), ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, self, 570425344)));
}
??對比兩個block的c++形式郁轻,我們發(fā)現(xiàn)翅娶,在__ViewController__viewDidLoad_block_impl_0
中多了一個類型指針ViewController *
,在他的賦值方法__ViewController__viewDidLoad_block_impl_0
中也多了一個入?yún)?code>ViewController *_self
??我們再看_I_ViewController_viewDidLoad
方法中是怎么調(diào)用personBlock
的,是向self發(fā)送了setPersonBlock
消息故觅,并且把self
和personBlock
地址以及personBlock
內(nèi)部方法的實現(xiàn)地址傳過去厂庇。
再看personBlock
方法實現(xiàn)__ViewController__viewDidLoad_block_func_0
里面渠啊,ViewController *self = __cself->self;
這里__cself
指的是personBlock
输吏,__cself->self
指的就是ViewController
了,然后向這里的self
發(fā)送setName
消息替蛉。
??personBlock
里面有強(qiáng)引用viewcontroller
贯溅,viewController
又強(qiáng)引用personBlock
,這樣就造成循環(huán)引用了躲查。
循環(huán)引用
來看一張相當(dāng)簡陋的圖
在這張圖里面介紹的兩種情況它浅,想想除了這兩種情況還有其他的么?這里我不說了镣煮,只是幫你理解姐霍,幫你分析,怎么找到這個環(huán)典唇。
解決方法
這個解決方法不用說镊折,大家也知道,weak
和strong
介衔,爛大街了都恨胚,為什么用了這兩個就好了呢?你不想看看用了weak
和strong
炎咖,編譯后C++代碼發(fā)生了什么變化赃泡?
Come On
- (void)viewDidLoad {
[super viewDidLoad];
self.name = @"VenpleD";
__weak typeof(ViewController) *weakSelf = self;
self.personBlock = ^{
__strong typeof(ViewController) *strongSelf = weakSelf;
strongSelf.name = @"HelloWorld";
};
}
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
typeof(ViewController) *__weak weakSelf;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, typeof(ViewController) *__weak _weakSelf, int flags=0) : weakSelf(_weakSelf) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
/**personBlock方法實現(xiàn)**/
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
typeof(ViewController) *__weak weakSelf = __cself->weakSelf; // bound by copy
__attribute__((objc_ownership(strong))) typeof(ViewController) *strongSelf = weakSelf;
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)strongSelf, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_mm_hb3kj211641625fw0pq0xlkrkk1cf__T_ViewController_b406fe_mi_1);
}
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_mm_hb3kj211641625fw0pq0xlkrkk1cf__T_ViewController_b406fe_mi_0);
/**轉(zhuǎn)換weak指針**/
__attribute__((objc_ownership(weak))) typeof(ViewController) *weakSelf = self;
/**調(diào)用personBlock**/
((void (*)(id, SEL, Person))(void *)objc_msgSend)((id)self, sel_registerName("setPersonBlock:"), ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, weakSelf, 570425344)));
}
??對比上面沒有用weak和strong的,我們發(fā)現(xiàn)self的指針修飾變成了typeof(ViewController) *__weak
乘盼,居然變成weak
類型的(我們知道默認(rèn)不加是strong
類型的)升熊,這樣我們的圖就變成了下面這個樣子,也就解決了循環(huán)引用的問題了绸栅。
??到了這里可能大家還有關(guān)于為什么block內(nèi)部要再用strong指針強(qiáng)引用一次外部weak指針级野。好,我解釋一下
??我們回頭看personBlock
實現(xiàn)__ViewController__viewDidLoad_block_func_0
中阴幌,objc_msgSend
方法的對象和消息分別是strongSelf
和setName
勺阐,我們知道,strong
一次矛双,就會對這個對象增加一次引用計數(shù)渊抽,這樣做,保證了方法正常執(zhí)行议忽。在這個方法里面懒闷,strongSelf
是個局部變量,生命周期是函數(shù)末尾,也就是執(zhí)行完objc_msgSeng
以后就會將這個對象(指ViewController)的引用計數(shù)減1愤估,帮辟,如果執(zhí)行完personBlock
,personBlock
引用計數(shù)為0正常釋放玩焰,自然他也不會對ViewController
強(qiáng)引用由驹,弱引用指針自動斷開,viewController
也就很好的釋放了昔园。
看到這里蔓榄,理解了么?
??這只是我的個人理解默刚,如果大家有什么見解甥郑,可以一起探討,一起學(xué)習(xí)