Как правильно отправлять функции в Swift?

Я продолжал пытаться, но я просто не понимаю. Я новичок в программировании, поэтому почти каждый новый шаг - это эксперимент. В то время как у меня нет проблем с отправкой нормальных закрытий без аргументов/возвратов, я пока не понял, как обращаться с функциями, которые принимают (несколько) аргументы и возвращаются в конце.

Чтобы получить логику правильной "работы вокруг", было бы здорово, если бы кто-нибудь мог опубликовать практический пример, чтобы я мог видеть, все ли у меня все в порядке. Я был бы очень благодарен за любую помощь... Если какой-нибудь другой практический пример лучше иллюстрирует эту тему, пожалуйста, продолжайте свой собственный!

Предположим, мы хотели бы асинхронно отправить следующую функцию в фоновую очередь с низким приоритетом (или я делаю ошибку, пытаясь реализовать диспетчеризацию при определении функции вместо ожидания до ее вызова из другого места?!):

func mutateInt(someInt: Int) -> Int {
    "someHeavyCalculations"
    return result
}

или функция с несколькими аргументами, которая дополнительно вызывает первую функцию в некоторой точке (все в фоновом порядке):

func someBadExample(someString: String, anotherInt: Int) -> Int {
    "someHeavyStuff"
    println(testString)
    mutateInt(testInt)
    return result
}

или UI-функцию, которая должна быть обеспечена для запуска только в главной очереди (просто фиктивный пример):

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {        
    let sectionInfo = self.fetchedResultsController.sections?[section] as NSFetchedResultsSectionInfo       
    return sectionInfo.numberOfObjects
}

Ответ 1

Скажем, у вас была какая-то функция:

func calculate(foo: String, bar: Int) -> Int {
    // slow calculations performed here

    return result
}

Если вы хотите сделать это асинхронно, вы можете обернуть его примерно так:

func calculate(foo: String, bar: Int, completionHandler: @escaping (Int) -> Void) {
    DispatchQueue.global().async {
        // slow calculations performed here

        completionHandler(result)
    }
}

Или, альтернативно, если вы хотите, чтобы обработчик завершения всегда вызывался в основной очереди, вы также можете сделать это для себя:

func calculate(foo: String, bar: Int, completionHandler: @escaping (Int) -> Void) {
    DispatchQueue.global().async {
        // slow calculations performed here

        DispatchQueue.main.async {           
            completionHandler(result)
        }
    }
}

Для работы, выполняемой в фоновом режиме, вы можете использовать другую приоритетную фоновую очередь или использовать свою собственную пользовательскую очередь или собственную очередь операций. Но эти детали не являются существенными для вопроса.

Что имеет значение, так это то, что сама эта функция не возвращает никакого значения, даже несмотря на то, что основная синхронная функция выполняет. Вместо этого это асинхронное представление передает значение обратно через закрытие completionHandler. Таким образом, вы будете использовать его так:

calculate(foo: "life", bar: 42) { result in
    // we can use the `result` here (e.g. update model or UI accordingly)

    print("the result is = \(result)")
}

// but don't try to use `result` here, because we get here immediately, before
// the above slow, asynchronous process is done

(FYI, все приведенные выше примеры - Swift 3. Для версии Swift 2.3 см. предыдущую версию этого ответа.)