· 前言
无意中写的demo遇到了这个问题,引出了block的三种类型以及编译器在ARC环境下对block进行优化的知识点,让自己对block以及ARC的内存管理有了更深刻的印象,特此写下来给需要帮助的iOSCoder
· 问题描述
如下所示,在一个UIButton中,我用了一个weak指针和一个strong指针接收一个block,在clickActionHandle方法执行block之前,将strong指针置为nil。
理想情况:随着strong指针的销毁,weak指针也会随之置为nil而不执行block。
实际情况:weakCCBack发生坏内存访问崩溃
#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导致执行clickActionHandle方法时,发生了坏内存访问的错误。打印了两个指针的地址才发现时ARC对block的赋值进行了优化。
self.strongCCBack = <__NSMallocBlock__: 0x600002617b40>
self.weakCCBack =<__NSStackBlock__: 0x7ffee6405200>
callBack = <__NSStackBlock__: 0x7ffee6405200>
·使用strong指针接收栈类型的block,编译器会将栈block进行一次拷贝操作生成一个新的堆Block保存在strong指针中。这么做的原因很容易理解:因为存储在栈中的值出了作用域以后会被系统自动回收,而block是一个等待着被执行的代码块,如果init方法结束后参数中的block(此block为栈block)会被立即回收,那么在执行block的时候会发生坏内存访问的情况,为了保证安全执行block,strong指针指向stackBlock(栈)的时候会生成一个mallocBlock(堆)副本,以确保能安全执行block。
·而使用weak指针指向栈block时,编译器未做任何优化,即便栈block被回收,weak指针也不会指向nil,继而导致执行block的时候会产生坏内存访问。
网友评论