[英]Am I capturing self in this nested function? The compiler does not fire a warning
我找不到任何有關此的官方文檔,並且那里有不同的意見。
在以下情況下,一切都很好。
final class MyVC: UIViewController {
var space: Space!
private let tableView = MenuCategoriesTableView()
private let tableViewHandler = MenuCategoriesTableViewHandler()
override func viewDidLoad() {
super.viewDidLoad()
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
tableView.dataSource = tableViewHandler
tableView.delegate = tableViewHandler
tableViewHandler.didSelectRow = { [unowned self] option in
let category = option.makeCategory()
if category.items.count > 0 {
let controller = MenuItemsViewController()
controller.title = option.rawValue
controller.space = self.space
self.show(controller, sender: self)
} else {
// whatever
}
}
}
}
但是,如果我進行以下更改,我不再需要使用unowned self
,但我仍然擔心捕獲自我。 我應該擔心嗎? 如果不是,為什么?
final class MyVC: UIViewController {
...etc...
override func viewDidLoad() {
super.viewDidLoad()
...etc...
func categorySelected(_ option: MenuOption, _ category: MenuCategory) {
let controller = MenuItemsViewController()
controller.title = option.rawValue
controller.space = space
show(controller, sender: self)
}
tableViewHandler.didSelectRow = { option in
let category = option.makeCategory()
if category.items.count > 0 {
categorySelected(option, category)
} else {
// whatever
}
}
}
}
當您將閉包分配給tableViewHandler.didSelectRow
時,您分配給它並保留該閉包捕獲的任何內容。
self
保留tableViewHandler
。
因此,危險在於您將在閉包內引用self
。 如果你這樣做,那就是一個保留周期。
這可能不是因為明確地提到了self
。 任何提及self
的屬性或方法都是對self
的隱式引用。
好的,因此,讓我們檢查一下閉包。
您沒有在閉包的主體中隱式或顯式地提及self
。 但是,您確實調用了本地方法categorySelected
。 因此,您捕獲此方法。
並且categorySelected
確實提到了self
。 因此,它捕獲self
(因為每個 function 都是一個閉包)。
因此存在一個潛在的保留周期,您應該繼續說unowned self
以防止保留周期。
(我認為編譯器在這里無法通過警告保留周期來幫助您;這太復雜了。這是您必須通過人為原因解決的問題。)
我做了一些調查,如果你使用引用self
的內部 function ,你確實會得到一個保留周期。
這是一個例子:
typealias ClosureType = () -> ()
class Test {
var closure:ClosureType?
let value = 42
func setClosure1() {
self.closure = {
print ("from setClosure1")
}
}
func setClosure2() {
self.closure = {
[unowned self] in
let v = self.value
print ("from setClosure2 - value: \(v)")
}
}
func setClosure3() {
func innerFunc() {
// [unowned self] in // not allowed, compile error (sometimes even crashes)
let v = value
print ("value: \(v)")
}
self.closure = {
[unowned self] in // compiler warning: "Capture [self] was never used"
print ("from setClosure3")
innerFunc()
}
}
deinit {
print ("Deinit")
}
}
如果您使用setClosure1
(trivial) 或setClosure2
(capture 子句),則不會發生保留循環:
if (1==1) {
let t = Test()
t.setClosure1()
t.closure?()
} // deinit called here
但是如果你調用setClosure3
, deinit
將不會被調用:
if (1==1) {
let t = Test()
t.setClosure3()
t.closure?()
} // deinit NOT called here
沒有直接的方法可以解決這個問題; 如您所見,在內部 function 中使用[unowned self]
會導致編譯器錯誤,在setClosure3
中使用它會導致警告。
不過,有一種方法可以解決這個問題 - 您可以使用第二個閉包,而不是使用內部 function,您可以在其中指定[unowned self]
捕獲子句:
func setClosure4() {
let innerClosure:ClosureType = {
[unowned self] in
let v = self.value
print ("value: \(v)")
}
self.closure = {
print ("from setClosure4")
innerClosure()
}
}
// ...
if (1==1) {
let t = Test()
t.setClosure4()
t.closure?()
} // deinit called here
結論:Swift 應該允許內部函數中的捕獲子句。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.