Я не могу понять причину, какой смысл добавлять эту функциональность.
Метод, который должен быть передан в качестве аргумента или вызываться другим методом
func add(_ a : Int, _ b : Int) -> Int {
return a + b
}
при вызове функции из другой функции
func average(_ a : Int, _ b : Int) -> Int{
return add(a, b) / 2
}
при передаче функции в качестве аргумента другой функции
func averageArg(_ plus: (Int, Int) -> Int, _ a: Int, _ b : Int) -> Int {
return plus(a, b) / 2
}
Этот код является просто примером. Я просматривал документацию Swift и наткнулся на эту концепцию «тип функции как параметр».
Например, без функции "postProcess" в качестве аргумента вам пришлось бы реализовывать древовидные функции: getAndAddData, getAndSubData и getAndMulData.
func add(_ a:Int, _ b:Int) -> Int {
return a + b
}
func sub(_ a:Int, _ b:Int) -> Int{
return a - b
}
func mul(_ a:Int, _ b:Int) -> Int{
return a * b
}
func getAndProcessData(_ postProcess:(Int, Int) -> Int, _ a:Int, _ b:Int) -> Int {
let a2 = ExampleClass.get(a)
let b2 = ExampleClass.get(b)
return postProcess(a2, b2)
}
func exampleFunc(a:Int, b:Int) {
let getAndAdd = self.getAndProcessData(self.add(_:_:), a, b)
let getAndSub = self.getAndProcessData(self.sub(_:_:), a, b)
let getAndMul = self.getAndProcessData(self.mul(_:_:), a, b)
}
Причина использования функции в качестве аргумента заключается в том, что она делает вашу функцию более гибкой. Это позволяет абонент решать, что делает plus
, передавая функцию или замыкание, которое объединяет два значения Int
, чтобы вернуть значение Int
.
Например, предположим, что вызывающая сторона хочет, чтобы plus
перемножил значения:
print(averageArg(*, 5, 6))
15
или возьмите max
двух значений (передав замыкание):
print(averageArg({ max($0, $1) }, 1, 100))
50
Лучшим примером этого является функция Swift sorted(by:)
. sorted
принимает замыкание, которое определяет, что означает areInIncreasingOrder
. Это позволяет вам сортировать массив в порядке возрастания и убывания, просто изменяя переданную функцию:
[1, 3, 2, 4].sorted(by: <)
[1, 2, 3, 4]
[1, 3, 2, 4].sorted(by: >)
[4, 3, 2, 1]
Вот пример использования функции:
func shortNamesFirst(_ name1: String, _ name2: String) -> Bool {
if name1.count < name2.count {
return true
} else if name1.count > name2.count {
return false
} else {
// when names are the same length, sort
// them alphabetically
return name1 < name2
}
}
["Chuck", "Bob", "Bill", "Jo", "Ed", "Alexander"].sorted(by: shortNamesFirst)
["Ed", "Jo", "Bob", "Bill", "Chuck", "Alexander"]
Автору sorted
не нужно предоставлять разные версии sorted
для каждого типа заказа, который может захотеть пользователь. Пользователь сам решает, что для него значит sorted
.
Таким образом, преимущество заключается в следующем: можно передавать разные функции с разными функциями, если они имеют одинаковую структуру, определенную в параметре вызывающей функции, которая ограничивается, когда мы вызываем функцию по ее имени в другой функции.
Хорошо. Спасибо. Это было полезно. :)
Ваш пример - не очень убедительное использование передачи замыканий. Однако представьте, что реализация является переменной, т.е. вы не знаете, что должно произойти при определенных обстоятельствах, работа с замыканиями — это большое преимущество. Возьмите этот пример:
func doSomething(with: AnyObject, whenFails failureClosure: ((Error) -> Void)? = nil) {
var error: Error?
// do stuff, maybe setting the error to something...
if let error = error,
let failureClosure = failureClosure {
failureClosure(error)
}
}
Это позволяет вызывающему объекту doSomething
, но обрабатывать сбои функции особым образом, передавая замыкание.
Это становится действительно полезным в асинхронных ситуациях, когда результат вашей функции используется для каких-то действий, но вы не можете запустить выполнение в том же потоке (например, сетевые вызовы).
func doSomethingAsynchronously(completion completionClosure: (() -> Void)? = nil) {
// Do asynchronous stuff
if let completionClosure = completionClosure {
completionClosure()
}
}
Это особенность Swift, которая используется нечасто, но время от времени может пригодиться. В вашем примере это действительно не имело бы смысла, но давайте найдем сценарий, в котором это действительно имеет смысл.
Давайте сначала посмотрим, что означает _ plus: (Int, Int) -> Int
. Этот фрагмент кода означает, что ваша функция averageArg
принимает любые закрытие или функция, которые принимают два целочисленных параметра и выдают целое число в качестве вывода. Поскольку add(...)
соответствует этому требованию (два аргумента integer
и выдает integer
в качестве вывода), вы можете передать его в качестве аргумента.
Но давайте посмотрим на пример, где такой параметр имеет смысл. Допустим, мы пишем интерфейсный код для приложения, которое показывает отзывы об отелях. В этом приложении нам нужно где-то написать функцию fetchReviews(_ serverResponse: ([Review]) -> Void)
, которая получает все отзывы об отелях с какого-то удаленного сервера:
struct Review {
let author: String
let rating: Int
}
func fetchReviews(_ completionHandler: @escaping ([Review]) -> Void) {
// Lets do a request to the server on a separate thread, so that the user can keep
// interacting with the app. The app doesn't freeze.
// Making qos .userInitiated will ensure that user interaction will be more important than
// our backend request.
DispatchQueue.global(qos: .userInitiated).async {
// Do some request to the server....
//...
// Lets pretend we have received a response, and we are turning the response into an array of reviews
completionHandler([Review]())
}
}
Поскольку может быть (иногда большая) задержка между тем, когда сервер отвечает и дает все обзоры, и между вызовом fetchReviews
, для пользователя было бы нехорошо, если бы приложение просто зависло. Вместо этого вы можете сделать запрос к серверу в отдельном потоке, чтобы пользователь мог продолжать использовать приложение, а приложение не зависало (см. часть DispatchQueue.global
).
Однако мы по-прежнему хотим отображать все отзывы пользователю после получения ответа от сервера. Добавив параметр _ serverResponse
, мы можем уведомить любого, кто звонил fetchReviews
, как только мы действительно получили ответ сервера. Делая это таким образом, пользователь может продолжать взаимодействовать с приложением, и как только обзоры станут доступны, мы сможем показать их пользователю!
Можете ли вы дать нам некоторый контекст? Где используется этот код и с какой целью?