【问题标题】:Should I use `weak self` when making asynchronous network request?发出异步网络请求时应该使用“弱自我”吗?
【发布时间】:2020-12-27 23:54:28
【问题描述】:

这是我从网络中获取一些数据的方法:

func fetchProducts(parameters: [String: Any],
                success: @escaping ([Product]) -> Void) 

正如您所注意到的,它具有转义闭包。这是我在 ViewModel 中调用上述方法的方式:

service.fetchProducts(parameters: params, success: { response in
        self.isLoading?(false)
        /// doing something with response
}) 

问题是我应该弱还是强捕获self?为什么?我想我可以强烈地捕捉到它。因为,fetchProducts 是一个以闭包为参数的函数。但是,我可能错了。但是,从另一个角度来看,我认为它应该是弱的。因为,ViewModel 强引用 serviceservice 强引用 success 闭包,强引用 self(即 ViewModel)。它创建了保留周期。但是在取消初始化拥有 ViewModel 的 ViewController 之后,无论如何都会调用 ViewModel 的 deinit。这意味着没有保留周期。为什么?

【问题讨论】:

    标签: ios swift memory-management automatic-ref-counting retain-cycle


    【解决方案1】:

    只要你的viewmodel是一个类,你就必须弱捕获self,否则你会有一个强引用循环。由于fetchProducts 是异步的,它的成功闭包可能会在您的视图模型已经被释放后执行 - 或者如果闭包没有持有对它的强引用,则可能会被释放。异步闭包中的强引用将阻止视图模型被释放。

    如果你在类中调用service.fetchProducts 并在异步闭包中访问self,你确实需要[weak self]。如果您要在值类型(structenum)中执行此操作,或者如果您没有在闭包内访问 self,则不需要 [weak self] - 在值类型中,您甚至不能做[weak self]

    service.fetchProducts(parameters: params, success: { [weak self] response in
            self?.isLoading?(false)
            /// doing something with response
    }) 
    

    【讨论】:

    • 是的,ViewModel 是引用类型。但是,你如何解释即使有强 self 也会调用 ViewModel 的 deinit 呢?
    • @neo 是否创建强引用循环取决于何时执行闭包以及何时释放对视图模型的所有其他引用。只有最后一个引用在闭包内时,强引用循环才会关闭。因此,例如,如果您的网络请求需要 10 秒才能返回结果,但您的用户在 5 秒后已经导航离开存储您的视图模型的视图,那么由于使用了强 self.你可以通过弱捕获自我来防止这种情况。
    • 好的,但是假设我的网络请求需要 10 秒。用户从该屏幕导航出去。当然,我的 ViewModel 不能被取消。但在success 闭包被执行后,它将被释放。我哪里错了?
    • @DávidPásztor 例如,如果您的网络请求花费了 10 秒.. 这并不完全正确,我有这种情况,当网络完成并得到响应时,一切都已成功释放。
    • @neo,不,即使请求返回,它也不会被释放。那就是你错了。创建内存泄漏是因为一旦该闭包之外的所有引用都被释放,视图模型就应该被释放。但是,持有对视图模型的引用的闭包会创建一个不会被释放的僵尸对象。
    猜你喜欢
    • 1970-01-01
    • 2016-01-27
    • 1970-01-01
    • 2021-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多