[英]Avoiding [weak self] for simple operations?
對於短期運行的操作,避免[weak self]
是否可以接受? 例如, URLSession
將保留來自dataTask(with:completion:)
的閉包:
final class ViewController: UIViewController {
let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
let decodedString = String(bytes: data, encoding: .utf8)
DispatchQueue.main.async {
self.label.text = decodedString
}
}.resume()
}
}
在這種情況下,閉包會強烈地捕獲self
,這意味着即使這個ViewController
被閉包保存在內存中。 URLSession
將保持關閉直到數據任務完成,這意味着ViewController
的生命周期可能會延長,直到dataTask
完成。
在這種情況下,我們是否應該使用捕獲列表來避免這種行為? 我的推理是否正確,這里沒有參考周期?
ViewController 的生命周期可能會延長,直到 dataTask 完成
所以問題是這是否一致。 這甚至可能是一件好事。 如果可以,那很好,並且不需要weak self
,因為沒有保留周期,因為 url 會話是共享的。
但是當 url 會話是一個實例屬性並且有一個真正的委托時,事情要復雜得多,你真的可以得到一個保留周期,因為會話保留了它的委托,這可能會保留會話。
如果您擔心引用周期,則在使用 URL 請求時通常不會得到引用周期。 問題是 URL 請求遲早會完成(幾分鍾后)並且您的控制器會被釋放。 引用周期只是暫時的,不會導致內存泄漏。
問題是,即使用戶已經關閉控制器並且不再顯示它,您是否希望將控制器保留在內存中。 它可能不會造成任何問題,但仍然很浪費。 您持有不需要且無法重用的內存。
另請注意,您可能實際上希望在控制器關閉時取消正在運行的請求,以避免發送/接收不再需要的數據。
在我看來,您不應該太擔心參考周期,而應該更多地考慮所有權。 強引用意味着擁有某些東西。 該請求沒有理由“擁有”控制器。 反之亦然——控制器擁有並管理請求。 如果沒有所有權,為了清楚起見,我會使用weak
。
我的推理是否正確,這里沒有參考周期?
這里沒有引用循環。 ViewController
不保留dataTask
完成處理程序。 您可以將其視為 iOS 保持對視圖控制器和完成處理程序的強引用,並且完成處理程序也保持對視圖控制器的強引用。 沒有從視圖控制器返回到完成處理程序或任何引用完成處理程序的對象鏈的強引用,因此您是無循環的。 在UIView.animate
尋找相同的模式,您再次將閉包發送到 iOS,而不是將它們存儲在本地。
對於短期運行的操作,避免
[weak self]
是否可以接受?
工作的持續時間不是一個因素。 兩個相關的問題是:
拿這個例子:
class BadVC: UIViewController {
private lazy var cycleMaker: () -> Void = { print(self) }
override func loadView() {
view = UIView()
cycleMaker()
}
}
BadVC
在這里設法創建了一個引用循環,一旦加載它的視圖就永遠不會被破壞。 cycleMaker()
將在納秒內執行的事實並沒有使我們免於內存泄漏。
務實地說,還有第三個問題:
您可以手動中斷參考循環。 例如:
class StillBadVC: UIViewController {
private lazy var cycleMaker: () -> Void = { print(self) }
override func loadView() {
view = UIView()
cycleMaker()
}
func breakCycle() {
cycleMaker = { }
}
}
在這里,我們處於危險之中,因為StillBadVC
有一個對cycleMaker
的強引用,而cycleMaker
捕獲了對StillBadVC
的強引用。 只要有人記得調用breakCycle()
,循環就會中斷,此時視圖控制器將刪除其對cycleMaker
強引用,從而允許cycleMaker
解除分配。 但是,如果有人忘記調用breakCycle()
,則循環不會中斷。 調用一個叫做breakCycle()
的方法通常不是使用視圖控制器的契約的一部分,所以我們預計StillBadVC
在實踐中會導致內存泄漏。
你絕對應該在這里使用[weak self]
,不是因為強引用循環的任何風險,而是因為這個閉包的存在僅用於更新標簽。 編寫故意將視圖控制器及其視圖保留在內存中的代碼是沒有意義的,因此您可以更新視圖中可能已被解除且不再可見的標簽。
weak
關鍵字的存在不僅僅是為了避免強引用循環,而是為了准確表示對象所有權和管理對象生命周期。 您不應該僅僅為了保存與[weak self]
捕獲列表相關的幾個擊鍵而歪曲對象所有權圖。
我想你已經在這里得到了答案。 這不是參考循環。
但是為了建立一種系統的方法,我在這里的建議更簡單。 忘記考慮泄漏和其他東西。
考慮所有權和流量控制,然后是內存管理
這個思考過程不僅可以幫助您區分真正的內存泄漏和非泄漏。 它還可以幫助您更好地設計和閱讀您的代碼,而不是盲目地到處轉儲[weak self]
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.