您的当前位置:首页正文

Swift - 如何避免闭包中出现野指针

来源:华佗小知识
下面我们来分析一下实际工作中的案例:
错误的写法 外界调用
为什么会造成野指针呢?

首先我们来看看clickedButtonAtIndexBlock是什么时候调用的,进入HGBaseAlertView找到相关的代码:

08457a29.png

分析:

1.在clickedButtonAtIndexBlock被调用后,self.joinBeforeHandler?()会立刻被执行,然后发起GroupAPIService.joinGroup(...)这个请求,当clickedButtonAtIndexBlock闭包走完之后,我们可以在上图看到程序又立马调用了dismiss,然后alertView会从window中移除;

2.alertView的引用计数变为了0(为什么是0呢,因为我们为了避免循环引用用unowned修饰了self,所以闭包内部没有对alertView进行强引用,引用计数也就不会+1);

3.这样就导致alertView会很快被释放(注意:不是立刻被释放,要看它所在的作用域的代码什么时候执行完毕);

4.问题来了,很大概率会出现GroupAPIService.joinGroup(...)这个请求回来之后alertView不在了,并且在闭包内部对self的引用类型为unowned(相当于OC中的__unsafe_unretained),再继续访问self的属性(执行self.joinAfterHandler?(succeed))就会引发crash(野指针)

该怎么解决呢?

1.首先大家可能会想到用weak去代替unowned

虽然可以解决crash,但是self.joinAfterHandler?(succeed)很大概率不会被执行了,这并不能解决我们的问题,因为除非网络错误否则我们一定想要self.joinAfterHandler?(succeed)被执行的.

2.想办法延长alertView的生命,OC中我们可以用__strong,swift中怎么做呢?

正确写法

如上图,我们可以在发起异步请求之前用一个指针handler去指向self.joinAfterHandler,虽然此时handler也是弱指针,但是我们注意到GroupAPIService.joinGroup(...)的回调闭包并没有对handler进行unowned/weak修饰,所以handler被捕获到异步请求的回调闭包之后,闭包会对这个弱指针进行强引用,会导致alertView的引用计数+1,也就保证了请求回来后alertView依然存在,避免了野指针的出现;如果你理解OC的__weak__strong搭配使用原理的话,这里就很好理解.