Block 中使用成员变量引发crash
有如下一段代码块,在执行这段代码时有时候会触发crash
void(^test)() = ^() {
__strong typeof (weakSelf)strongSelf = weakSelf;
strongSelf -> _xx = xxx; //引发crash
strongSelf.yy = yyy; // 不会引发crash
[strongSelf doSomething];
[strongSelf doMoreThing];
}
__weak typeof (self)weakSelf = self;
[self test];
现状:
在self因某些情况被释放掉,变成nil,然后执行blcok体。
结论:
使用使用nil.yyy不会引发crash,使用nil -> _xxx 会引发crash。
解决方案:
在block中__strong后判断strongSelf是否为nil,为空的话直接return
原因:
在block中使用self -> _xxx,如果在执行到block时,self被释放了,在block中就算是使用了__strong typeof () ,这个时候self是nil,并没有强持有self,使用nil -> _xxx 便会引发crash。
详细解释及分析:
在block外使用weak是为了弱引用self,self强引用block,如果在block中在强引用self,会导致死循环。故在声明时使用weak,在block体正式被执行的时候,使用strong,这里主要是为了防止block在执行的过程中,如果self是被弱引用,可能会由于外界失去引用而被释放,导致block中的代码错误执行。
在block执行的时候,执行到strong,如果 WeakSelf 还没有变成 nil,那么就会 retain self,让 self 在 block 执行期间不会变为 nil。这样上面的 doSomething 和 doMoreThing 要么全执行成功,要么全失败,不会出现一个成功一个失败,即执行到中间 self 变成 nil 的情况。
上面的情况之所以会crash,是由于在block执行之前self就被释放了,变成nil,在执行block ,对nil 进行了强以后用,属于无效操作。关于为什么nil -> _xxx会触发而 nil.yyy正常,看注2.
注1:block引用的是weak(self) 里面声明的 weakSelf. 后面使用到的self是strong重新声明的局部变量.
注2:OC有空指针发消息不会崩溃的语言特性,原因是OC的函数调用都是通过objc_msgSend进行消息发送来实现的,相对于C和C++来说,对于空指针的操作会引起Crash的问题,而objc_msgSend会通过判断self来决定是否发送消息,如果self为nil,那么selector也会为空,直接返回,所以不会出现问题。.yyy这种属性的赋值和调用实质上是调用set和get方法,故发送消息没有触发,而直接使用指针赋值触发crash