捕获异步网络请求中的self弱引用或无主引用

5

每当我进行异步网络请求时,可能会出现请求到达时self已经为nil的情况(例如ViewController已经被解除引用)。

为了防止这种情况,通常我会将self声明为weak:

    future.onSuccess(context: Queue.main.context, callback: { [weak self] result in
        if let strongSelf = self {
            // Do some stuff with self, which is now guaranteed to be not nil
            // strongSelf.someMethod()
        }
    })

或者我可以将 self 捕获为 unowned:

    future.onSuccess(context: Queue.main.context, callback: { [unowned self] result in
            // Do some stuff with self
            // self.someMethod()
    })

我不在意请求是否返回,因为当请求返回时,如果 ViewController 已经被关闭,我没有办法展示请求的结果。因此,我不想通过闭包来保持 self 的“存活”状态。

我的问题是 - 在这种情况下,仅仅将 self 设置为 unowned 是否足够?还是说我必须一直使用 [weak self] 来进行 nil 检查?如果请求到达时,self 已经被设置为 nil,那么闭包还会继续存在并执行吗?会在访问 nil 时触发运行时错误吗?或者,在这种情况下,我可以忘记 weak 和 unowned 因为当 self 被释放时,闭包也会被释放,所以不存在访问 nil 的危险?

注:我知道关于这个主题已经有很多问答了,但是我在不关心异步到达时的情况下没有找到答案。


1
如果我们假设future的生命周期与self无关,则使用unowned将是不安全的,因为当future及其对闭包的引用仍然存在时,self可能已经被释放。 - Mike Pollard
2个回答

8
如果你在没有使用 weakunowned 关键字的情况下捕获 self,那么你的视图控制器将被闭包所保留,并在闭包结束前不会释放。 weakunowned 的区别在于,weak 是一个可选类型,需要条件绑定来进行安全检查。另一方面,unowned 的安全性不如 weak 高,它假设视图控制器仍然存活,如果视图控制器已经释放,则会导致崩溃。更多信息请参见这个回答

是的,在大多数情况下我会使用它。 - Adam
当我不使用weak或unowned时:我不明白当self被释放时,是什么使闭包“存活”。是谁或什么使闭包保持活跃状态?没有任何对它的引用。 - Darko
例如,如果您使用 NSURLSession,会话本身管理引用。闭包内部的内容并不重要,也不会影响其生命周期。 - Adam
啊,所以闭包的调用者就是保持闭包存活的实例? - Darko
1
谢谢,解答了我的问题! - Darko

5

在这种情况下,您不应该使用unowned,因为如果对象在调用完成之前被释放,您将拥有一个悬空引用,它没有设置为nil。如果您在请求完成时尝试使用此引用,则应用程序将崩溃。

另外,如果您真的不再需要响应,请在视图控制器被释放时取消请求。如果您使用NSURLSession,则始终会得到一个NSURLSessionTask引用,可以取消该引用。因此,人们经常将在闭包中使用[weak self]模式与保存对NSURSessionTask的额外弱引用的代码相结合,并在deinit条件下进行有条件的取消。


谢谢你提供取消请求的想法。我正在使用AFNetworking,但从未研究过这个功能。 - Darko
如果您正在使用AFHTTPSessionManager(您不应再使用AFHTTPRequestOperationManager),则会获得NSURLSessionTask引用,您可以将其保存为可选项并稍后取消。 - Rob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接