Swift @scaping и обработчик завершения

Я пытаюсь понять "Закрытие" Свифта более точно.

Но @escaping и Completion Handler слишком сложны для понимания

Я искал много сообщений Свифта и официальных документов, но чувствовал, что этого все еще недостаточно.

Это пример кода официальных документов

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

Первый - для хранения замыкания, второй - для асинхронных рабочих целей.

Ниже приведены мои вопросы:

Во-первых, если doSomething выполняется, то someFunctionWithEscapingClosure будет выполняться с параметром замыкания, и это замыкание будет сохранено в массиве глобальных переменных.

Я думаю, что закрытие {self.x = 100}

Как self в {self.x = 100}, которая сохраняется в глобальной переменной completionHandlers может подключаться к instance, что объект SomeClass?

Во-вторых, я понимаю, что такая someFunctionWithEscapingClosure такая.

Для хранения локальных переменных закрытия completionHandler к глобальной переменной "completionHandlers we using @escaping" ключевое слово!

без @escaping слова @escaping возвращает someFunctionWithEscapingClosure, локальная переменная completionHandler удалит из памяти

@escaping держать это закрытие в памяти

Это правильно?

Наконец, мне просто интересно узнать о существовании этой грамматики.

Может быть, это очень элементарный вопрос.

Если мы хотим, чтобы какая-то функция выполнялась после какой-то конкретной функции. Почему бы нам просто не вызвать какую-либо функцию после вызова конкретной функции?

Каковы различия между использованием вышеупомянутого шаблона и использованием функции обратного вызова?

Ответ 1

Прежде всего, я хочу сказать "Очень хороший вопрос :)"

Обработчик завершения:

Предположим, что пользователь обновляет приложение во время его использования. Вы определенно хотите уведомить пользователя, когда это будет сделано. Возможно, вы захотите открыть окно с надписью: "Поздравляю, теперь вы можете полностью насладиться!"

Итак, как вы запускаете блок кода только после завершения загрузки? Кроме того, как вы анимируете определенные объекты только после того, как контроллер представления был перемещен к следующему? Что ж, мы собираемся выяснить, как спроектировать его как босса. Исходя из моего обширного словарного списка, обработчики завершения обозначают

Делайте вещи, когда все сделано

Для более подробной информации, пожалуйста, посетите этот блог.

Эта ссылка дает мне полную ясность о обработчиках завершения (с точки зрения разработчика, она точно определяет то, что нам нужно понять).

@escaping замыкания:

Когда вы передаете замыкание в аргументах функции, используйте его после того, как тело функции будет выполнено и вернет компилятор обратно. Когда функция завершается, область действия переданного замыкания существует и существует в памяти, пока замыкание не будет выполнено.

Есть несколько способов избежать замыкания в содержащей функции:

  • Хранение: Когда вам нужно сохранить замыкание в глобальной переменной, выполняется свойство или любое другое хранилище, которое существует в памяти после вызывающей функции, и возвращает компилятор обратно.

  • Асинхронное выполнение: Когда вы выполняете закрытие асинхронно в очереди отправки, очередь будет удерживать закрытие в памяти для вас, и может быть использовано в будущем. В этом случае вы не знаете, когда будет выполнено замыкание.

Когда вы попытаетесь использовать замыкание в этих сценариях, компилятор Swift покажет ошибку:

error screenshot

Для большей ясности по этой теме вы можете проверить этот пост на Medium.

Я надеюсь, что вы получите хорошее понимание по этой ссылке.

Если у вас все еще есть какие-либо вопросы (но сначала обязательно прочитайте эту ссылку построчно; она очень хорошо объяснена), то не стесняйтесь делиться своим комментарием.

Спасибо, продолжайте писать, если этот ответ нужно обновить

Ответ 2

Вот небольшой класс примеров, которые я использую, чтобы напомнить себе, как работает @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 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' 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()
        }
    }

}

Ответ 3

функция

Вы определяете функцию с помощью ключевого слова func. Функции могут принимать много параметров и не возвращать ни одного, один или несколько параметров

закрытие

Замыкания - это автономные блоки функциональности, которые можно передавать и использовать в вашем коде. Замыкания в Swift похожи на блоки в C и Objective-C и на лямбды в других языках программирования.

Функции и замыкания и типы первого класса в swift:

  • назначить функцию/замыкание локальной переменной
  • передать функцию/замыкание в качестве аргумента
  • вернуть функцию/закрытие

Экранирующее закрытие против не экранирующего закрытия

  • Не @noescape non-escaping closure @noescape - это замыкание, которое вызывается внутри функции, в которую оно было передано, то есть до его возврата.

    Хорошим примером non-escaping closure является sort function массива, подобная sorted(by: (Element, Element) → Bool). Закрытие принимает два параметра и возвращает Bool, который определяет результат функции сортировки. Закрытие вызывается во время выполнения расчетов сортировки.

    * @noescape был атрибутом в @noescape swift 2. Это устарело от swift 3. The @Noescape attribute is applied by default in Swift 3 . Because closures are by default . Because closures are by default не являются экранирующими in Swift 3, escaping closures need to be marked as such. And the in Swift 3, escaping closures need to be marked as such. And the атрибут @escaping 'позволяет нам это делать.

  • @escaping escaping closure @escaping - это замыкание, которое вызывается после возврата функции, которую оно было передано. Другими словами, он переживает функцию, которой он был передан. Типичные случаи использования этого:

    • Асинхронные вызовы; сетей.
    • Функции хранятся в виде переменных; продумайте действия и предоставленные обратные вызовы.
    • Планирование задач в очереди отправки.


    Хорошим примером escaping closure является completion handler. Многие функции, которые запускают асинхронную операцию, принимают в качестве обработчика завершения аргумент замыкания. Функция возвращается после запуска операции, но замыкание не вызывается до тех пор, пока операция не будет завершена - замыкание должно быть завершено, чтобы быть вызванным позже.

Подробнее читайте здесь - Средний пост, Средний пост, документы

Ответ 4

import UIKit
import Alamofire

//Модель

class ShortlistCountResponse : Decodable {
    var response : String?
    var data : ShortlistcountData?

}
class ShortlistcountData : Decodable {

    var totalpropFavcount : Int?
    var totalprojFavcount : Int?

}

//Общее определение класса......

static func fetchGenericData<T: Decodable>(urlString: String,params : [String:Any], completion: @escaping (T) -> ()) {
        let url = urlString
        let headers = ["Content-Type": "application/x-www-form-urlencoded", "Accept":"application/json"]
        Alamofire.request(url, method: .post, parameters:params, encoding: URLEncoding.default, headers: headers).responseJSON { response in
            print(response.request?.urlRequest ?? "")
            print(params)
            print(response.data ?? "")
            print(response.value ?? "")
            switch(response.result) {
            case .success(_):
                if let data = response.data{
                    do {
                        let gotData = try JSONDecoder().decode(T.self, from: data)
                        completion(gotData)

                    }
                    catch let jsonErr {
                        print("Error serializing json:", jsonErr)
                        ActivityIndicator.dismissActivityView()

                    }
                    DispatchQueue.main.async {
                        ActivityIndicator.dismissActivityView()
                    }
                }
                break
            case .failure(_):
                print(response.result.error ?? "")
                ActivityIndicator.dismissActivityView()


                break

            }
        }
}

//забавный звонок

override func viewDidLoad() {
    super.viewDidLoad()

            let userID = ""
            let languageID = ""
            let params = ["userID":userID,"languageID":languageID]
            var appDelegate: AppDelegate?
            Service.fetchGenericData(urlString: "your url...", params: params) { (shortlistCountResponse : ShortlistCountResponse) in
             print(shortListCountResponse.data.totalprojFavcount ?? 0)

            }
}