[Из песочницы] Анонимные функции в Swift
Эта публикация является конспектом соответствующего раздела замечательной книги «iOS 8 Programming Fundamentals with Swift» Matt Neuburg, O’Reilly, 2015. Статья, описывающая использование анонимных функций, может быть интересной новичкам или пригодится как шпаргалка для более продвинутых разработчиков.Рассмотрим пример:
func whatToAnimate () {//self.myButton является кнопкой в интерфейсе Self.myButton.frame.origin.y += 20 } func whatToDoLater (finished: Bool) { printLn («finished: \(finished)») } UIView.animateWithDuration ( 0.4, animations: whatToAnimate, completion: whatToDoLater) В этом участке кода есть некоторая странность. Я объявляю функции whatToAnimate и whatToDoLater только для того, чтобы передать эти функции на следующую строку кода. И действительно, имена этих функций мне больше ни для чего не нужны — ни имена, ни эти функции больше никогда не будут повторно использованы. Было бы хорошо передавать только тела этих функций, без необходимости декларации их имен.Такие функции называются анонимными, они имеют право на существование и часто используются в Swift.
Создание анонимной функции Чтоб создать анонимную функцию мы делаем две вещи: Создаем тело функции, включая окружающие фигурные скобки, но без декларации функции. Если необходимо, то в первой строке внутри фигурных скобок, после команды in, описываем список аргументов функции и тип возвращаемой переменной. Рассмотрим на примере, как преобразовать функцию в анонимную. Код исходной функции: func whatToAnimate () { Self.mybutton.frame.origin.y += 20 } Вот пример анонимной функции, которая выполняет ту же задачу. Мы переместили список аргументов и возвращаемый тип внутрь фигурных скобок: { () → () in self.myButton.frame.origin.y += 20 } Теперь, когда мы знаем, как создать анонимную функцию, посмотрим, как мы можем ее использовать. В нашем вызове animateWithDuration, точка, в которой мы вызваем функции, является местом, где мы передаем аргументы этой функции. Значит, мы можем создавать анонимную функцию прямо в этой же точке, например так: UIView.animateWithDuration (0.4, animations: { () → () in self.myButton.frame.origin.y += 20 } , completion: { (finished: Bool) → () in printLn («finished: \(finished)») }) Анонимные функции очень часто используются в Swift, так что следует убедиться, что мы легко читаем и пишем такой код. Анонимные функции настолько общеупотребительны и настолько важны, что существуют некоторые сокращения, используемые при их создании. Рассмотрим такие случаи.Опускаем тип возвращаемой переменной Если компилятор уже знает, какой тип переменной вернет функция, то мы можем опустить оператор arrow, тип и спецификацию переменной. UIView.animateWithDuration (0.4, animations: { () in self.myButton.frame.origin.y += 20 }, completion: { (finished: Bool) in printLn («finished: \(finished)») }) Опускаем строку с оператором in, когда в ней нет аргументов Если анонимная функция не принимает аргументов и если тип возвращаемой переменной может быть опущен, то и строка с командой in может быть опущенной целиком: UIView.animateWithDuration (0.4, animations: { self.myButton.frame.origin.y += 20 }, сompletion: { (finished: Bool) in printLn («finished: \(finished)») }) Опускаем типы аргументов Если анонимная функция принимает аргументы, и их типы известны компилятору, то эти типы могут быть опущены: UIView.animateWithDuration (0.4, animations: { self.myButton.frame.origin.y += 20 }, completion: { (finished) in printLn («finished: \(finished)») }) Опускаем скобки Если типы аргументов могут быть опущены, то и скобки вокруг этих аргументов могут быть опущены: UIView.animateWithDuration (0.4, animations: { self.myButton.frame.origin.y += 20 }, completion: { finished in printLn («finished: \(finished)») }) Опускаем строку с in, даже если в ней есть аргументы Если возвращаемый тип может быть опущен, и если типы аргументов известны компилятору, вы можете опустить строку с in и ссылаться на аргументы прямо из тела анонимной функции используя имена $0, $1 и так далее по порядку: UIView.animateWithDuration (0.4, animations: { self.myButton.frame.origin.y += 20 }, completion: { printLn («finished: \($0)») }) Опускаем имена аргументов Если ваша анонимная функция не нуждается в аргументах, то вы можете опустить аргументы, заменяя их имена знаком подчеркивания в строке с in: UIView.animateWithDuration (0.4, animations: { self.myButton.frame.origin.y += 20 }, completion: { _ in printLn («finished!») }) Примечание. Если ваша анонимная функция принимает аргументы, вы должны их так или иначе использовать. Вы можете опустить строку с in и использовать аргументы по их именам типа $0, $1 и так далее, или вы можете сохранить строку с in и заменить аргументы на знак подчеркивания, но вы не можете одновременно и опускать строку с in, и не использовать аргументы по их именам $0, $1. Если вы так поступите, ваш код не будет скомпилирован.
Опускаем имена аргументов вызывающей функции Если ваша анонимная функция является последним параметром, который передается в вызове, то вы можете закрыть вызов скобкой до последнего параметра, а затем поставить тело анонимной функции без указания имен аргументов. Такой способ называется замыкающей функцией (trailing function). UIView.animateWithDuration (0.4, animations: { self.myButton.frame.origin.y += 20 }) { _ in printLn («finished!») }) Опускаем скобки вызывающей функции Если вы используете замыкающую функцию и вызываемая вами функцияне принимает других параметров, кроме передаваемой вами функции, вы можете опустить пустые скобки в вызове. Для примера я объявлю и вызову другую функцию: func doThis (f: () → ()) { f () } doThis { // тут опускаем скобки printLn («Hello») } Это единственная ситуация, в которой вы можете опустить скобки в вызове функции.Опускаем ключевое слово return Если тело анонимной функции состоит только из одного утверждения и это утверждение состоит из возвращаемой величины с ключевым словом return, то ключевое слово return может быть опущено. Иначе говоря, в ситуации когда ожидаем, что функция вернет величину, если тело анонимной функции состоит только из одного утверждения, Swift подразумевает, что это утверждение является выражением, чье значение будет возвращено из анонимной функции: func sayHello () → String { return «Hello» } func performAndPrint (f: () → String) { let s = f () printLn (s) } performAndPrint { sayHello () //подразумевая return sayHello () в функции performAndPrint } Практическое использование Рассмотрим пример. У нас есть массив переменных и нам нужно получить новый массив, состоящий из тех же величин, умноженных на 2, вызванных при помощи метода map. Метод массива map принимает функцию, которая принимает аргумент и возвращает переменную того же типа, как и элемент массива. Наш массив состоит из переменных Int, следовательно мы будем передавать методу map функцию, которая принимает параметр Int и возвращает Int. Мы могли бы написать целую функцию, например такую let arr = [2, 3, 4, 5, 6] func doubleMe (i: Int) → { return i*2 } let arr2 = arr.map (doubleMe) Однако такой подход не полностью использует возможности Swift. Нам больше нигде не нужна функция doubleMe, так что она вполне может стать анонимной. Тип возвращаемой ею переменной известен, так что нам не нужно указывать его. Тут всего лишь один параметр, и мы собираемся использовать его, но нам не нужна строка с in, если мы ссылаемся на параметр как $0. Тело нашей функции состоит из одного утверждения, и это return, так что мы можем опустить return. И функция map не принимает каких-либо других параметров, так что мы можем опустить скобки и сразу после анонимной функции указывать имя: let arr = [2, 3, 4, 5, 6] let arr2 = arr.map {$0×2} Если начать пользоваться анонимными функциями, то вы обнаружите, что часто пользуетесь преимуществами, которыми они предоставляют. Вдобавок, вы часто будете сокращать объем кода (но не в ущерб коду), помещая целые анонимные функции в одну строку с вызовом функции.