簡體   English   中英

處理URLSession時是否總是需要[弱自己]?

[英]Is [Weak self] always needed when dealing with URLSession?

我無法弄清楚在這種情況下我是否需要使用[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()
    }
}

Service.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 docs(強調我的)中所述:

創建任務后,通過調用resume()方法啟動它。 然后會話保持對任務的強引用, 直到請求完成或失敗 ; 您不需要維護對任務的引用,除非它對您的應用程序的內部簿記有用。

會議強烈提到了這項任務。 該任務強烈提到了關閉。 閉包具有對ViewModel的強引用。 只要ViewModel沒有對該任務的強引用(它不在您提供的代碼中),那么就沒有循環。

問題是您是否要確保ViewModel繼續存在足夠長的時間以便執行閉包。 如果您(或不關心),那么您可以使用簡單的強引用。 如果要阻止任務使ViewModel保持活動狀態,則應使用弱引用。

這就是您需要考慮參考周期的方法。 沒有一般規則“在這里使用weak ”。 當你的意思是什么時,你會使用weak ; 當你不希望這個閉包在self被釋放之前保持self 如果它創造一個循環,那尤其如此。 但是沒有一般的答案是“這會創造一個循環嗎”。 這取決於哪些部分包含引用。

這也指出了您當前的API設計不盡如人意的地方。 你在init傳遞didLoadData 這很可能會創建參考周期並強制您的調用者使用weak 相反,如果你在loadUser()上使didLoadDdata成為一個完成處理程序,那么你可以避免這個問題並使調用者的生活更輕松。

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

(你當前的API在任何情況下都有競爭條件。你可以在設置didLoadData之前啟動loadUser() 。你可能假設在設置dataDidLoad之前完成處理程序不會完成,但是沒有真正的承諾這可能是真的,但它最多是脆弱的。)

您的代碼的問題不在於使用URLSession,而在於您在視圖控制器中保留了一個函數:

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

如果didLoadData函數隱式或顯式地提到self (即ViewModel實例),則除非你說weak selfunowned self否則你有一個保留周期和內存泄漏。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM