для чего нужны автозамыкания swift

Интервью — 10 вопросов про Swift. Часть 3

Профессиональная программа «iOS Разработчик» — 5 месяцев Best Practice по разработке мобильных приложений с помощью Swift 5. Лучших выпускников ждут на собеседованиях 12 компаний-партнёров OTUS, поэтому публикуем перевод заключительной статьи из серии «iOS Interview Questions (Swift)», где рассмотрим ещё десяток вопросов, ответы на которые помогут вам при трудоустройстве.

для чего нужны автозамыкания swift

1. Что такое замыкания и где их можно использовать?

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

2. Что такое убегающие и неубегающие замыкания (escaping/nonescaping closures)?

@nonescaping (стандартные) замыкания:

@escaping (убегающие) замыкания:

3. Укажите, какие типы коллекций доступны в Swift?

для чего нужны автозамыкания swift

4. Как определяется базовый класс в Swift?

В Swift классы, которые не наследуются от базового класса, и классы, которые вы определяете без указания суперкласса, автоматически становятся базовыми классами.

5. Что такое деинициализаторы и как они пишутся в Swift?

Деинициализатор объявляется непосредственно перед освобождением памяти, занимаемой экземпляром класса. Деинициализатор пишется с ключевым словом deinit. Он используется, если нужно выполнить какие-либо действия или чистку перед освобождением памяти, занимаемой объектом.

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

Деинициализатор записывается без скобок и не принимает никаких параметров.

6. Когда используются двойные вопросительные знаки «??»?

Этот оператор называется оператором nil-объединения. Он используется для того, чтобы задать значение по умолчанию, если опционал равен nil.

6. В чем разница между ‘?’ И ‘!’?

Символ «?»

Символ «!»

7. Что такое псевдоним типа в Swift?

Объявление псевдонима типа вводит именованный псевдоним существующего типа в программу. Объявления псевдонимов типов объявляются с помощью ключевого слова typealias.

typealias name = existing type

Вы можете использовать typealias для большинства типов в Swift, например:

8. В чем разница между функциями и методами в Swift?

Метод — это функция, связанная с классом, структурой или перечислением. Это относится как к методам экземпляров, так и к методам типов.

Функция — объявлена в глобальной области видимости и не относится ни к какому типу.

Функции могут быть определены вне классов или внутри классов/структур/перечислений, в то время как методы должны быть определены внутри и быть частью классов/структур/перечислений.

9. Какой синтаксис у внешних параметров?

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

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

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

10. Можно ли переопределять структуры и перечисления в Swift?

Нельзя создать подкласс структуры или перечисления, как и переопределить их. Потому что структура является типом значения, и компилятор должен знать ее точный размер во время компиляции, что переопределение делает невозможным.

Чтобы найти предыдущие части, переходите по ссылкам Часть 1, Часть 2, Все о замыканиях, Все о свойствах

Вот и всё! Уверены, переводы пригодятся не только студентам курса «iOS Разработчик», а также многим пользователям Хабра. Желаем всем профессиональных успехов и ждём в ближайших группах наших авторских онлайн-программ!

Источник

Документация

для чего нужны автозамыкания swift для чего нужны автозамыкания swift

для чего нужны автозамыкания swift для чего нужны автозамыкания swift

Автозамыкания (autoclosures)

2037 views 09.10.2015 admin_ 1

Автозамыкания — замыкания, которые автоматически создаются для заключения выражения, которое было передано в качестве аргумента функции. Такие замыкания не принимают никаких аргументов при вызове и возвращают значение выражения, которое заключено внутри нее. Синтаксически вы можете опустить круглые скобки функции вокруг параметров функции, просто записав обычное выражение вместо явного замыкания.

Автозамыкания позволяют вам откладывать вычисления, потому как код внутри них не исполняется, пока вы сами его не запустите. Это полезно для кода, который может иметь сторонние эффекты или просто является дорогим в вычислительном отношении, потому что вы можете контролировать время исполнения этого кода. Пример ниже отображает как замыкания откладывают вычисления:

Заметка

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

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Источник

Используем замыкания в Swift по полной

Несмотря на то, что в Objective-C 2.0 присутствуют замыкания (известные как блоки), ранее эппловский API использовал их неохотно. Возможно, отчасти поэтому многие программисты под iOS с удовольствием эксплуатировали сторонние библиотеки, вроде AFNetworking, где блоки применяются повсеместно. С выходом Swift, а также добавлением новой функциональности в API, работать с замыканиями стало чрезвычайно удобно. Давайте рассмотрим, какими особенностями обладает их синтаксис в Swift, и какие трюки можно с ними «вытворять».

для чего нужны автозамыкания swift

Продвигаться будем от простого к сложному, от скучного к веселому. Заранее приношу извинения за обильное использование мантр «функция», «параметр» и «Double», но из песни слов не выкинешь.

Часть 1. Вводная

1.1. Объекты первого класса

Для начала укрепимся с мыслью, что в Swift функции являются носителями гордого статуса объектов первого класса. Это значит, что функцию можно хранить в переменной, передавать как параметр, возвращать в качестве результата работы другой функции. Вводится понятие «типа функции». Этот тип описывает не только тип возвращаемого значения, но и типы входных аргументов.
Допустим, у нас есть две похожие функции, которые описывают две математические операции сложения и вычитания:

Их тип будет описываться следующим образом:

Прочесть это можно так: «Перед нами тип функции с двумя входными параметрами типа Double и возвращаемым значением типа Double
Мы можем создать переменную такого типа:

Код, описанный выше, выведет в консоли:
3.0
-1.0

1.2. Замыкания

Давайте теперь передадим в качестве третьего аргумента не переменную, а анонимную функцию, заключив ее в фигурные <> скобки. Переданный таким образом параметр и будет называться замыканием:

Часть 2. Веселая.
Синтаксический сахар и неожиданные «плюшки»

Авторы Swift приложили немало усилий, чтобы пользователи языка могли писать как можно меньше кода и как можно больше тратить свое драгоценное время на чтение Хабра размышления об архитектуре проекта. Взяв за основу наш пример с арифметическими операциями, посмотрим, до какого состояния мы сможем его «раскрутить».

2.1. Избавляемся от типов при вызове.

Во-первых, можно не указывать типы входных параметров в замыкании явно, так как компилятор уже знает о них. Вызов функции теперь выглядит так:

2.2. Используем синтаксис «хвостового замыкания».

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

2.3. Не используем ключевое слово «return».

Приятная (в некоторых случаях) особенность языка заключается в том, что если код замыкания умещается в одну строку, то результат выполнения этой строки автоматичеси будет возвращен. Таким образом ключевое слово «return» можно не писать:

2.4. Используем стенографические имена для параметров.

Идем дальше. Интересно, что Swift позволяет использовать так называемые стенографические (англ. shorthand) имена для входных параметров в замыкании. Т.е. каждому параметру по умолчанию присваивается псевдоним в формате $n, где n — порядковый номер параметра, начиная с нуля. Таким образом, нам, оказывается, даже не нужно придумывать имена для аргументов. В таком случае весь «заголовок» замыкания уже не несет в себе никакой смысловой нагрузки, и его можно опустить:

Согласитесь, эта запись уже совсем не похожа на ту, которая была в самом начале.

2.5. Ход конем: операторные функции.

Все это были еще цветочки. Сейчас будет ягодка.
Давайте посмотрим на предыдущую запись и зададимся вопросом, что уже знает компилятор о замыкании? Он знает количество параметров (2) и их типы (Double и Double). Знает тип возвращаемого значения (Double). Так как в коде замыкания выполняется всего одна строка, он знает, что ему нужно возвращать в качестве результата его выполнения. Можно ли упростить эту запись как-то еще?
Оказывается, можно. Если замыкание работает только с двумя входными аргументами, в качестве замыкания разрешается передать операторную функцию, которая будет выполняться над этими аргументами (операндами). Теперь наш вызов будет выглядеть следующим образом:

Красота!
Теперь можно производить элементарные операции над нашими операндами в зависимости от некоторых условий, написав при этом минимум кода.

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

Или битовые операции:

Swift — в некотором роде забавный язык программирования. Надеюсь, статья будет полезной для тех, кто начинает знакомиться с этим языком, а также для тех, кому просто интересно, что там происходит у разработчиков под iOS и Mac OS X.
___________________________________________________________________

UPD.: Реальное применение

Некоторые пользователи высказали недовольство из-за отсутствия примеров из реальной жизни. Буквально вчера натолкнулся на задачу, которая может быть элегантно решена с использованием коротких замыканий.

Если вам нужно создать очередь с приоритетом, можно использовать двоичную кучу (binary heap). Как известно, это может быть как MinHeap, так и MaxHeap, т.е. кучи, где в корне дерева находится минимальный или максимальный элемент соотвественно. Базовая реализация MinHeap от MaxHeap будет отличаться по сути только проверочными сравнениями при восстановлении инварианта двоичной кучи после добавления/удаления элемента.

Теперь для того, чтобы создать классы MinHeap и MaxHeap нам достаточно унаследоваться от BinaryHeap, а в их конструкторах просто явно указать, какое сравнение применять. Вот так будет выглядеть наши классы:

Источник

Мьютексы и захват замыканиями в Swift

для чего нужны автозамыкания swift

В этой статье речь пойдёт об отсутствии потокового выполнения (threading) и инструментов синхронизации потоков в Swift. Мы обсудим предложение о внедрении «многопоточности» (concurrency) в Swift и то, как до появления этой возможности потоковое выполнение в Swift будет подразумевать использование традиционных мьютексов и общего изменяемого состояния (shared mutable state).

Использовать мьютекс в Swift не особенно сложно, но на этом фоне хотелось бы выделить тонкие нюансы производительности в Swift — динамическое выделение памяти во время захвата замыканиями. Мы хотим, чтобы наш мьютекс был быстрым, но передача замыкания для исполнения внутри мьютекса может снизить производительность в 10 раз из-за дополнительных расходов памяти. Давайте рассмотрим несколько способов решения данной проблемы.

Отсутствие потокового выполнения (threading) в Swift

Когда Swift был впервые анонсирован в июне 2014 года, у него было два очевидных упущения:

А потоковое выполнение по большей части всё ещё игнорируется Swift. Вместо языковых средств обеспечения потокового выполнения, Swift включает в себя модуль Dispatch (libdispatch, aka Grand Central Dispatch) на всех платформах, и неявно предлагает нам использовать Dispatch вместо того, чтобы ожидать помощи от языка.

Делегирование ответственности библиотеке, идущей в поставке, кажется особенно странным по сравнению с другими современными языками, такими как Go и Rust, в которых примитивы (primitives) потокового выполнения и строгая потокобезопасность (соответственно) стали основными свойствами своих языков. Даже свойства @synchronized и atomic в Objective-C кажутся щедрым предложением по сравнению с отсутствием чего-либо подобного в Swift.

Какова причина такого очевидного упущения в этом языке?

Будущая «многопоточность» в Swift

Я упоминаю это предложение, чтобы подчеркнуть, что разработчики Swift в будущем хотели бы сделать что-то в отношении многопоточности, но прошу иметь в виду, что говорит разработчик Swift Джо Грофф: «этот документ — всего лишь предложение, а не официальное заявление о направлении развития».

Это предложение появилось с целью описать ситуацию, когда, например, в Cyclone или Rust ссылки не могут быть разделены между потоками выполнения. Независимо от того, похож ли результат на эти языки, кажется, в Swift планируется устранить общую память потоков, за исключением типов, реализующих Copyable и передаваемых через строго управляемые каналы (в предложении называемые Stream ’ами). Также появится разновидность сопрограммы (coroutine) (в предложении называемая Task ’ами), которая будет вести себя как асинхронные блоки отправки (asynchronous dispatch blocks), которые можно ставить на паузу/возобновлять.

Звучит хорошо, но когда ожидать многопоточность в Swift? В Swift 4? В Swift 5? Не скоро.

Таким образом, сейчас нам это не помогает, а скорее даже мешает.

Влияние будущих функций на текущую библиотеку

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

Можно найти этому явные свидетельства, читая список рассылок Swift-Evolution:

Пытаемся найти быстрый мьютекс мьютекса общего назначения

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

Все эти проблемы делают pthread_mutex_lock / pthread_mutex_unlock единственным разумным, производительным и портируемым вариантом.

Мьютексы и подводные камни захвата замыканием

Как и большинство вещей в чистом языке C, pthread_mutex_t имеет довольно неуклюжий интерфейс, что помогает использовать обертку Swift (особенно для построения и автоматической очистки). Кроме того, полезно иметь “scoped”-мьютекс — который принимает функцию и выполняет её внутри мьютекса, обеспечивая сбалансированную «блокировку» и «разблокировку» с любой стороны функции.

Должно работать быстро, но это не так. Вы видите, почему?

Что именно «захвачено»? Давайте рассмотрим следующий пример:

Чтобы сделать что-то полезное внутри мьютекса, ссылка на protectedMutableState должна храниться в «контексте замыкания», который представляет собой данные, находящиеся в динамической памяти.

Это может показаться достаточно безобидным (в конце концов, захват — то, чем занимаются замыкания). Но если функция sync не может быть встроена в то, что её вызывает (потому что она находится в другом модуле или файле, а оптимизация всего модуля выключена), то при захвате будет выделяться динамическая память.

А мы этого не хотим. Чтобы избежать этого, передадим замыканию соответствующий параметр вместо того, чтобы его захватывать.

ПРЕДУПРЕЖДЕНИЕ: следующие несколько примеров кода становятся всё более нелепыми, и в большинстве случаев я предлагаю не следовать им. Я делаю это, чтобы продемонстрировать глубину проблемы. Прочтите главу «Другой подход», чтобы увидеть, что я использую на практике.

Так-то лучше… теперь функция работает на полной скорости (0,282 секунды для теста из 10 миллионов вызовов).

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

демонстрирует такую же низкую скорость оригинала, даже когда замыкание ничего не захватывает (на 1,371 секунде скорость падает ещё ниже). Для обработки своего результата это замыкание выполняет динамическое выделение памяти.

Мы вернулись к полной скорости, или достаточно близкой к ней (0,307 секунды для 10 миллионов вызовов).

Другой подход

Одним из преимуществ захвата замыканием является то, насколько легким он кажется. Элементы внутри захвата имеют одинаковые имена внутри и снаружи замыкания, и связь между ними очевидна. Когда мы избегаем захвата замыканием, и вместо этого пытаемся передать все значения в качестве параметров, мы вынуждены либо переименовывать все наши переменные, либо давать им теневые имена (shadow names) — что не способствует простоте понимания — и мы по-прежнему рискуем случайно захватить переменную, опять ухудшив производительность.

Давайте всё отложим и решим проблему по-другому.

Семафоры, не мьютексы?

Вы можете использовать dispatch_semaphore_t для создания конструкции наподобие мьютекса:

Оказывается, это примерно на треть быстрее мьютекса pthread_mutex_lock / pthread_mutex_unlock (0,168 секунды против 0,244). Но, несмотря на увеличение скорости, использование семафора для мьютекса является не самым лучшим вариантом для общего мьютекса.

Семафоры склонны к ряду ошибок и проблем. Наиболее серьезными из них являются формы инверсии приоритета. Инверсия приоритета — это тот же тип проблемы, из-за которого OSSpinLock стал использоваться под iOS, но проблема для семафоров немного сложнее.

При спин-блокировке инверсия приоритета означает:

Если вместо семафора был использован правильный мьютекс, то приоритет высокоприоритетного потока будет передан в поток с более низким приоритетом, пока высокоприоритетный ожидает мьютекса, удерживаемого низкоприоритетным потоком, — это позволяет низкоприоритетному потоку завершить свою работу и разблокировать высокоприоритетный поток. Однако семафоры не удерживаются потоком, поэтому передачи приоритета произойти не может.

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

Всё это может показаться немного запутанным — поскольку вы, вероятно, не создаете намеренно в своих программах потоки с разными приоритетами. Тем не менее, фреймворки Cocoa добавляют немного сложностей: они повсеместно используют очереди отправки (dispatch queues), и каждая очередь имеет «класс QoS». А это может привести к тому, что очередь будет работать с другим приоритетом потока. Если вы не знаете очередность каждой задачи в программе (включая пользовательский интерфейс и другие задачи, поставленные в очередь с помощью фреймворков Cocoa), то неожиданно можете столкнуться с ситуацией многопоточного приоритета. Лучше этого избегать.

Применение

Файл CwlMutex.swift полностью самодостаточен, поэтому можно просто скопировать его, если это всё, что вам нужно.

Или же файл ReadMe.md содержит подробную информацию о клонировании всего репозитория и добавлении создающего его фреймворка в ваши проекты.

Заключение

Лучшим и безопасным вариантом мьютекса в Swift как под Mac, так и под iOS, остается pthread_mutex_t. В будущем Swift, вероятно, обзаведётся возможностью оптимизировать изолирующие (non-escaping) замыкания в стеке, или инлайнить за границами модулей. Любое из этих нововведений устранит присущие проблемы с Dispatch.sync, вероятно, сделав его лучшим вариантом. Но пока что он слишком неэффективен.

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

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

Потоковое выполнение, инлайнинг и оптимизация — всё это темы, в которых мы можем ожидать значительных изменений за рамками Swift 3. Однако, текущим пользователям Swift приходится работать в Swift 2.3 и Swift 3 — и в этой статье описывается текущее поведение в этих версиях при попытке получить максимум производительности при использовании scoped-мьютекса.

Дополнение: показатели производительности

10 миллионов раз был прогнан простой цикл: ввод мьютекса, увеличение счетчика и вывод мьютекса. «Медленные» версии DispatchSemaphore и PThreadMutex оказались скомпилированы как часть динамической структуры, отдельно от тестового кода.

Источник

Что такое @escaping в Swift замыканиях?

для чего нужны автозамыкания swift для чего нужны автозамыкания swift

для чего нужны автозамыкания swift для чего нужны автозамыкания swift

Что такое @escaping в Swift замыканиях?

Escaping Closures (сбегающие замыкания)

Ниже приведен пример несбегающего замыкания:

Как замыкание может сбежать?

Термин @escapin g может показаться вам непонятным, но фактическая реализация, позволяющая замыканию выжить в области действия вызывающей функции, очень проста. Чтобы выполняемое замыкание сохранилось после завершения функции, вы должны сохранить его в переменной, определенной вне функции.

Переменная для хранения замыкания

Назначение неcбегающего параметра ‘completion’ вместо @escaping замыкания.

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

После того, как мы получим данные о местоположении, мы вызываем замыкание с этой информацией.

А потом освобождаем его от обязанностей.

Вложенное замыкание

Вы получите следующее сообщение об ошибке компиляции, если забудете о сбегающем замыкании:

Сбегающее замыкание захватывает несбегающий параметр «closure».

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

Это приводит к циклу сильных ссылок.

Цикл сильных ссылок прервется, как только мы получим местоположение.

В случае сбоя цикл сильных ссылок остается (поскольку здесь мы ничего не делаем).

Заключение

Источник


Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *