下面我们来分析一下实际工作中的案例:
错误的写法 外界调用为什么会造成野指针呢?
首先我们来看看clickedButtonAtIndexBlock
是什么时候调用的,进入HGBaseAlertView
找到相关的代码:
分析:
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
搭配使用原理的话,这里就很好理解.