w Swift 3, wszystkie zamknięcia są dla ucieczki domyślnie
Nie, w Swift 3, tylko zamknięcie argumenty funkcji (tj wejścia funkcyjne, które są funkcjami siebie) są dla ucieczki przez domyślny (jak na SE-0103). Np
class A {
let n = 5
var bar :() -> Void = {}
func foo(_ closure:() -> Void) {
bar = closure // As closure is non-escaping, it is illegal to store it.
}
func baz() {
foo {
// no explict 'self.' required in order to capture n,
// as foo's closure argument is non-escaping,
// therefore n is guaranteed to only be captured for the lifetime of foo(_:)
print(n)
}
}
}
closure
jak w powyższym przykładzie jest nie ucieka, jest zakazane przechowywane chwytać, ograniczając w ten sposób jego żywotność do życia funkcją foo(_:)
. Oznacza to, że wszelkie przechwycone wartości nie zostaną przechwycone po wyjściu funkcji - co oznacza, że nie musisz martwić się o problemy, które mogą wystąpić podczas przechwytywania, takie jak zatrzymywanie cykli.
Jednakże zamknięcie przechowywać Obiekt (np bar
w powyższym przykładzie), stanowi ucieczki (to jest nonsensowne oznaczyć je @noescape
) jako życia nie ograniczony do danej funkcji - to (a zatem wszystkie przechwycone zmienne) pozostaną w pamięci tak długo, jak długo dana instancja pozostanie w pamięci. Może to łatwo prowadzić do problemów, takich jak zatrzymywanie cykli, dlatego konieczne jest użycie jawnego self.
, aby semantyka przechwytywania była jednoznaczna.
W rzeczywistości, w przypadku punktu, twój przykład kodu tworzy cykl zatrzymania momencie viewDidLoad()
miano, jak someClosure
mocno chwyta self
i self
silnie odwołuje someClosure
, jak jest to zapisane nieruchomość.
Oczywiście, jedynym miejscem, gdzie można oczekiwać, aby móc użyć atrybutu @noescape
jest na zamknięcie, które jest zmienna lokalna w funkcji. Takie zamknięcie miałoby przewidywalną trwałość, o ile nie jest przechowywane poza funkcją lub nie jest przechwytywane. Na przykład:
class A {
let n = 5
func foo() {
let f : @noescape() -> Void = {
print(n)
}
f()
}
}
Niestety, jak @noescape
jest usuwany w Swift 3, to nie będzie to możliwe (Co ciekawe jest to, że w Xcode 8 GM, to jest możliwe, ale daje ostrzeżenie amortyzację). Jako Jon Shier says będziemy musieli poczekać, aż zostanie on ponownie dodany do języka, co może, ale nie musi się zdarzyć.
Dziękujemy za wyjaśnienia! Aby wyjaśnić, kiedy sam kontroler widoku zostanie zwolniony, czy zamknięcie i widok również zostaną zwolnione, czy też spowodują wyciek pamięci? – beingadrian
@beingadrian Jeśli twoja własność 'someClosure' ma przypisane zamknięcie, które silnie przechwytuje' self' (na przykład w twoim przykładzie w 'viewDidLoad'), to faktycznie stworzy cykl zatrzymania, a zatem będzie przeciekał, jeśli nie ma innych silnych odniesienia do kontrolera widoku (możesz to zweryfikować, implementując 'deinit' z instrukcją' print' w klasie kontrolera widoku). Najprostszym rozwiązaniem byłoby przechwycenie 'self' jako' słabego' (zdefiniuj zamknięcie jako '{[słabe self] w ...}'). Następnie możesz użyć opcjonalnego łańcucha w zamknięciu, aby przypisać warstwę widoku, 'self? .view.layer = CALayer()' – Hamish
@beingadrian Innym potencjalnym rozwiązaniem jest przechwycenie 'self' w zamknięciu jako' unowned', jednak jest to potencjalnie niebezpieczne, ponieważ ulegnie awarii, jeśli zamknięcie zostanie wywołane po zwolnieniu instancji kontrolera widoku. Dlatego radziłbym robić to tylko wtedy, gdy 'someClosure' byłaby własnością' private' (wtedy nie ma szansy, aby inna klasa uzyskała odniesienie do niej). – Hamish