代码之家  ›  专栏  ›  技术社区  ›  iOSGeek

在处理会话时,总是需要[软弱的自我]吗?

  •  2
  • iOSGeek  · 技术社区  · 7 年前

    我不知道是否需要使用 [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()
        }
    }
    

    斯威夫特

    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)
            } 
        }
    }
    

    视图模型.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)
            }
       }
    }
    

    这里真的需要用户吗 [弱自我] ?当我们处理一个我们不知道闭包发生了什么的API时,是否有一个规则来检查它在一般情况下是否需要?或者这无关紧要(由我们决定)?

    2 回复  |  直到 7 年前
        1
  •  2
  •   Rob Napier    7 年前

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

    正如在 URLSessionDataTask 文档(强调我的):

    创建任务后,通过调用其resume()方法启动它。然后,会话保持对任务的强引用 直到请求完成或失败 ;您不需要维护任务的引用,除非它对应用程序的内部簿记有用。

    该会话对任务有很强的引用。这项任务对结束有很强的参考作用。这个结论对 视图模型 . 只要 视图模型 没有对任务的强引用(在您提供的代码中没有),那么就没有周期。

    问题是你是否想确保 视图模型 继续存在足够长的时间来执行闭包。如果你这样做(或者不在乎),那么你可以使用一个简单的强引用。如果要阻止任务保留 视图模型 活着,那么你应该使用弱引用。

    这就是你需要考虑参考周期的方式。没有一般规则“使用 weak 这里。”你用 虚弱的 当这就是你的意思的时候;当你不想让这个结束保持下去的时候 self 直到它被释放。如果它创造了一个循环,这尤其是正确的。但对于“这会创造一个循环吗?”并没有一般性的答案,这取决于哪些片段包含引用。

    这也指出了您当前的API设计可能不如它好的地方。你正在通过 didLoadData 在里面 init . 很可能会创建引用循环并强制调用方使用 虚弱的 . 如果不是你做的 didLoadDdata 上的完成处理程序 loadUser() 这样你就可以避免这个问题,让来电者的生活更轻松。

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

    (在任何情况下,您当前的API都有竞争条件。你开始了 加载程序() 之前 数据加载数据 可以设置。您可能假设完成处理程序在设置之前不会完成 dataDidLoad 但是没有真正的承诺。可能是真的,但充其量也很脆弱。)

        2
  •  0
  •   matt    7 年前

    代码的问题不在于使用URLSession,而在于您在视图控制器中保留了一个函数:

    class ViewModel {
        var didLoadData: ((User) -> Void)?
    }
    

    如果 didLoadData 功能说明 self (即viewModel实例)隐式或显式地,您有一个保留周期和内存泄漏,除非您说 weak self unowned self .