ios - 处理URLSession时是否总是需要[Weak self]?

标签 ios swift closures nsurlsession retain-cycle

我不知道在这种情况下是否需要使用 [weak self]

HTTPClient.swift:

struct HTTPClient {
    let session = URLSession.shared

    func get(url: URL, completion: @escaping (Data) -> Void) {
        session.dataTask(with: url) { data, urlResponse, error in
        completion(data) // assume everything will go well
      }.resume()
    }
}

服务.swift

struct Service {
    let httpClient: HTTPClient

    init(httpClient: HTTPClient = HTTPClient()) {
        self.httpClient = httpClient
    }

    func fetchUser(completion: @escaping (User) -> Void) {
        httpClient.get("urlToGetUser") { data in
          // transform data to User 
          completion(user)
        } 
    }
}

ViewModel.swift

class ViewModel {
   let service: Service
   let user: User?
   var didLoadData: ((User) -> Void)?

    init(service: Service) {
        self.service = service
        loadUser()
    }

   func loadUser() {
     service.fetchUser { [weak self] user in // is  [weak self] really needed ?
            self?.user = user
            self?.didLoadData?(user)
        }
   }
}

用户 [weak self] 真的需要它吗?当我们处理一个我们不知道闭包发生了什么的 API 时,是否有关于如何检查它是否通常需要的规则?或者这无关紧要(由我们决定)?

最佳答案

在您给出的示例中,[weak self] 可能是不必要的。如果 ViewModel 在请求完成之前被释放,这取决于您希望发生什么。

URLSessionDataTask 文档中所述(强调我的):

After you create a task, you start it by calling its resume() method. The session then maintains a strong reference to the task until the request finishes or fails; you don’t need to maintain a reference to the task unless it’s useful for your app’s internal bookkeeping.

session 对任务有很强的引用。该任务对闭包有很强的引用。闭包具有对 ViewModel 的强引用。只要 ViewModel 没有对任务的强引用(您提供的代码中没有),就没有循环。

问题是您是否要确保 ViewModel 继续存在足够长的时间以执行闭包。如果您这样做(或不关心),那么您可以使用简单的强引用。如果您想阻止任务使 ViewModel 保持事件状态,那么您应该使用弱引用。

这就是您需要考虑引用循环的方式。没有一般规则“在这里使用 weak”。当这就是你的意思时,你使用 weak;当你不希望这个闭包在它被释放之前保持 self 。如果它创建了一个循环,则尤其如此。但是对于“这是否会形成一个循环”并没有一个普遍的答案。这取决于哪些部分包含引用。

这也指出了您当前的 API 设计不尽如人意的地方。您正在 init 中传递 didLoadData。这很可能会创建引用循环并强制您的调用者使用 weak。相反,如果您将 didLoadDdata 作为 loadUser() 的完成处理程序,那么您就可以避免该问题并让调用者的生活更轻松。

func loadUser(completion: @escaping ((User?) -> Void)? = nil) {
    service.fetchUser {
        self?.user = user
        didLoadData?(user)
    }
}

(您当前的 API 在任何情况下都有竞争条件。您在 didLoadData 可以设置之前启动 loadUser()。您可能假设完成处理程序在您设置 dataDidLoad 之前不会完成,但并没有真正的 promise 。这可能是真的,但充其量是脆弱的。)

关于ios - 处理URLSession时是否总是需要[Weak self]?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53471621/

相关文章:

ios - 未以编程方式检测 Dismiss Popover

swift - 将 NSURL 数组保存到 NSUserDefaults

swift - 如何刷新 Swift 应用程序中的菜单项?

swift - 我如何使用 navigationController?.popViewController(动画 : true) inside of a closure with no warnings and errors?

ios - UIWebView 和本地存储

ios - 用于分析和登录的 GoogleService-Info.plist

iphone - 如何在我的应用程序中列出来自 iPhone 钥匙串(keychain)的证书?

swift - 使用不存在的 rawValue 初始化的枚举不会失败并返回 nil

javascript - 闭包和柯里化(Currying)中的引用如何在 js 中工作?

functional-programming - 函数式语言中如何使用闭包