· 前言
無意中寫的demo遇到了這個問題,引出了block的三種類型以及編譯器在ARC環(huán)境下對block進行優(yōu)化的知識點庆捺,讓自己對block以及ARC的內(nèi)存管理有了更深刻的印象,特此寫下來給需要幫助的iOSCoder
· 問題描述
如下所示,在一個UIButton中,我用了一個weak指針和一個strong指針接收一個block眶痰,在clickActionHandle方法執(zhí)行block之前,將strong指針置為nil梯啤。
理想情況:隨著strong指針的銷毀竖伯,weak指針也會隨之置為nil而不執(zhí)行block。
實際情況:weakCCBack發(fā)生壞內(nèi)存訪問崩潰
#import "CallBackButton.h"
@interface CallBackButton ()
@property (nonatomic,weak) void(^weakCCBack)(void);
@property (nonatomic,strong) void(^strongCCBack)(void);
@end
@implementation CallBackButton
-(instancetype)initWithCallBack:(void(^)(void))callBack{
if (self = [super init]) {
self.strongCCBack = callBack;
self.weakCCBack = callBack;
NSLog(@"self.strongCCBack = %@ self.weakCCBack =%@",self.strongCCBack,self.weakCCBack);
[self addTarget:self action:@selector(clickActionHandle) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(void)clickActionHandle{
self.strongCCBack = nil;
if (self.weakCCBack != nil) {
self.weakCCBack();
self.strongCCBack = nil;
}
}
· 原因解釋
同時用一個weak指針和一個strong指針指向init方法傳過來的block時因宇,當strong指針置為nil后七婴,weak指針沒有自動置為nil導(dǎo)致執(zhí)行clickActionHandle方法時,發(fā)生了壞內(nèi)存訪問的錯誤察滑。打印了兩個指針的地址才發(fā)現(xiàn)時ARC對block的賦值進行了優(yōu)化打厘。
self.strongCCBack = <__NSMallocBlock__: 0x600002617b40>
self.weakCCBack =<__NSStackBlock__: 0x7ffee6405200>
callBack = <__NSStackBlock__: 0x7ffee6405200>
·使用strong指針接收棧類型的block,編譯器會將棧block進行一次拷貝操作生成一個新的堆Block保存在strong指針中贺辰。這么做的原因很容易理解:因為存儲在棧中的值出了作用域以后會被系統(tǒng)自動回收户盯,而block是一個等待著被執(zhí)行的代碼塊嵌施,如果init方法結(jié)束后參數(shù)中的block(此block為棧block)會被立即回收,那么在執(zhí)行block的時候會發(fā)生壞內(nèi)存訪問的情況莽鸭,為了保證安全執(zhí)行block吗伤,strong指針指向stackBlock(棧)的時候會生成一個mallocBlock(堆)副本,以確保能安全執(zhí)行block硫眨。
·而使用weak指針指向棧block時足淆,編譯器未做任何優(yōu)化,即便棧block被回收礁阁,weak指針也不會指向nil巧号,繼而導(dǎo)致執(zhí)行block的時候會產(chǎn)生壞內(nèi)存訪問。