Я пытаюсь более точно понять «Закрытие» Swift.
Но @escaping
и Completion Handler
их слишком сложно понять
Я просмотрел множество сообщений Swift и официальных документов, но чувствовал, что этого все еще недостаточно.
Это пример кода официальных документов
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)
Я слышал, что есть два способа и причины использования @escaping
Первый предназначен для хранения закрытия, второй - для операционных целей Async.
Следующие мои вопросы :
Во-первых, если doSomething
выполняется, тоsomeFunctionWithEscapingClosure
будет выполняться с параметром закрытия, и это закрытие будет сохранено в массиве глобальных переменных.
Я думаю, что закрытие {self.x = 100}
Как self
в {self.x = 100}, сохраненном в глобальной переменной, completionHandlers
можно подключиться к instance
этому объектуSomeClass
?
Во-вторых, я так понимаю someFunctionWithEscapingClosure
.
Чтобы сохранить закрытие локальной переменной completionHandler
для глобальной переменной, 'завершенияHandlers we using
@ escaping` ключевое слово!
без возврата @escaping
ключевого слова someFunctionWithEscapingClosure
локальная переменная completionHandler
будет удалена из памяти
@escaping
держать это закрытие в памяти
Это правильно?
Наконец, я просто задаюсь вопросом о существовании этой грамматики.
Возможно, это очень элементарный вопрос.
Если мы хотим, чтобы какая-то функция выполнялась после определенной функции. Почему бы нам просто не вызвать какую-то функцию после вызова определенной функции?
В чем разница между использованием указанного выше шаблона и функцией обратного вызова с экранированием?
Вот небольшой класс примеров, которые я использую, чтобы напомнить себе, как работает @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() } } }
источник
@escaping
квалификаторов.i.e. escape the scope of this function.