Próbuję dokładniej zrozumieć „Zamknięcie” Swifta.
Ale @escaping
i Completion Handler
są zbyt trudne do zrozumienia
Przeszukałem wiele ogłoszeń Swift i oficjalnych dokumentów, ale czułem, że to wciąż za mało.
To jest przykład kodu oficjalnych dokumentów
var completionHandlers: [()->Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping ()->Void){
completionHandlers.append(completionHandler)
}
func someFunctionWithNoneescapingClosure(closure: ()->Void){
closure()
}
class SomeClass{
var x:Int = 10
func doSomething(){
someFunctionWithEscapingClosure {
self.x = 100
//not excute yet
}
someFunctionWithNoneescapingClosure {
x = 200
}
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
completionHandlers.first?()
print(instance.x)
Słyszałem, że są dwa sposoby i powody używania @escaping
Pierwszy służy do przechowywania zamknięcia, a drugi do celów operacyjnych Async.
Oto moje pytania :
Po pierwsze, jeśli doSomething
Wykonuje wtedy someFunctionWithEscapingClosure
będzie wykonanie z parametrem zamknięcia i zamknięcie zostanie zapisany w globalnej tablicy zmiennych.
Myślę, że zamknięcie to {self.x = 100}
Jak self
w {self.x = 100} ta zapisana w zmiennej globalnej completionHandlers
może połączyć się z instance
tym obiektem SomeClass
?
Po drugie, rozumiem w someFunctionWithEscapingClosure
ten sposób.
Aby przechowywać lokalne zamknięcie zmiennej completionHandler
w globalnym we using
słowie kluczowym „completeHandlers @ escaping”!
bez zwracania @escaping
słów kluczowych someFunctionWithEscapingClosure
zmienna lokalna completionHandler
zostanie usunięta z pamięci
@escaping
to zachować to zamknięcie w pamięci
Czy to jest poprawne?
Na koniec po prostu zastanawiam się nad istnieniem tej gramatyki.
Może to bardzo podstawowe pytanie.
Jeśli chcemy, aby jakaś funkcja była wykonywana po określonej funkcji. Dlaczego po prostu nie wywołamy jakiejś funkcji po wywołaniu określonej funkcji?
Jakie są różnice między użyciem powyższego wzorca a użyciem funkcji ucieczki zwrotnej?
Oto mała klasa przykładów, których używam, aby przypomnieć sobie, jak działa @escaping.
class EscapingExamples: NSObject { var closure: (() -> Void)? func storageExample(with completion: (() -> Void)) { //This will produce a compile-time error because `closure` is outside the scope of this //function - it's a class-instance level variable - and so it could be called by any other method at //any time, even after this function has completed. We need to tell `completion` that it may remain in memory, i.e. `escape` the scope of this //function. closure = completion //Run some function that may call `closure` at some point, but not necessary for the error to show up. //runOperation() } func asyncExample(with completion: (() -> Void)) { //This will produce a compile-time error because the completion closure may be called at any time //due to the async nature of the call which precedes/encloses it. We need to tell `completion` that it should //stay in memory, i.e.`escape` the scope of this function. DispatchQueue.global().async { completion() } } func asyncExample2(with completion: (() -> Void)) { //The same as the above method - the compiler sees the `@escaping` nature of the //closure required by `runAsyncTask()` and tells us we need to allow our own completion //closure to be @escaping too. `runAsyncTask`'s completion block will be retained in memory until //it is executed, so our completion closure must explicitly do the same. runAsyncTask { completion() } } func runAsyncTask(completion: @escaping (() -> Void)) { DispatchQueue.global().async { completion() } } }
źródło
@escaping
kwalifikacji.i.e. escape the scope of this function.