;
Версия Swift 1.2.принесла очень существенные и долгожданные изменения языка Swift :
— инкрементная компиляция. Это означает, что файлы с исходным кодом, которые не были изменены, больше не будут заново компилироваться по умолчанию,что значительно уменьшит время компиляции
— улучшенные сообщения об ошибках и повышение стабильности в Xcode
— “static” методы и свойства ( properties) теперь разрешены в classes (как псевдоним для “class final”). Теперь вам разрешается декларировать как «static» хранимые свойства (stored properties) в classes, которые сохраняются глобально и lazily инициализируются при первом доступе (то есть как глобальные переменные).
Теперь очень легко создавать Singletons — буквально одной строкой кода.
— в протоколах (Protocols) теперь требуется декларировать тип как “static” вместо “class”
— появился новый тип Set, который является взаимозаменяемым (bridging) с Objective-C классом NSSet,
также, как и String
, Array и
Dictionary
— взаимозаменяемы ( bridging) с их соответствующими Objective-C классами. Вы можете делать с Set в Swift все, что вы ожидаете делать с множествами вообще: проверять принадлежность значения к множеству, перечислять все элементы множества, выполнять операции union и intersect и т.д. Это существенное дополнения в стандартную библиотеку, которое закрыло огромную абстрактную «дыру», так как NSSet
не чувствовал себя комфортно в Swift
Ждем того же от NSDate
и других.
— изменен глобальный метод countElements на count
-приоритет (precedence) оператора ?? повышен, чтобы привязать его более жестко, чем логические операции и операции сравнения, но ниже, чем преобразование и операторы диапазона ( range). Это обеспечивает более полезное поведение выражений подобных этому:
— добавлена глобальная функция zip(), которая объединяет две последовательности в одну последовательность кортежей (tuple)
— Swift enums теперь могут экспортироваться в Objective-C , используя @objc атрибут.
Swift код:
[objc]
@objc enum Bear: Int {
Black, Grizzly, Polar
}
[/objc]
импортируется в Objective-C как:
[objc]
typedef NS_ENUM (NSInteger, Bear){
BearBlack, BearGrizzly, BearPolar
};
[/objc]
— улучшилось использование C union, битовых полей и других данных, не являющихся «родными» для Swift
— теперь инициализация let свойств отделена от их декларирования, но все равно они должны быть инициализированы перед использованием (как var); они могут быть только инициализированы, не переопределены или изменены после инициализации.
В Swift 1.1 можно так
Это возможный шаблон, в который можно «заворачивать» let свойства для упрощения их семантики в инициализаторах:
let x : SomeThing
if condition {
x = foo()
} else {
x = bar()
}
use(x)
— в Swift 1.2, «кастинг» вниз (subclass кастинг) можно выполнять либо как Optional с as?,
либо как “принудительный” с as!
. Если вы уверены относительно типа, то можете его усилить с помощью as!
подобно тому как мы используем «неявно развернутые Optionals ( implicitly-unwrapped Optional), и «ловить» ошибку на этапе компиляции, а не на этапе runtime.
Но самое большое изменение касается if let
Optional привязки (binding). Наконец-то!!. Конструкция if let
раньше использовалась для условного «развертывания» Optional значений. В прошлом вы могли «развернуть» только одно значение за раз, что приводило к так называемой “пирамиде смерти” if let
блоков, например такой:
Теперь это выглядит так
Порядок выполнения операций в этих двух примерах совершенно идентичен. Используя новый синтаксис, каждая привязка (binding) оценивается в порядке очереди, останавливаясь, если любая из попыток повязки приводит к nil
. Только после того, как Optional привязки успешно выполнены, проверяется предложение where
.
Более того, более поздние выражения привязки могут ссылаться на более ранние привязки. Это означает, что мы можем «заглядывать» внутрь экземпляров Dictionary
или выполнять «кастинг» AnyObject?
значений до специфического типа, а затем использовать его в других выражениях, и все это в единственном if let
предложении.
Вот как может выглядеть канонический пример JSON парсинга большого блока JSON данных в Swift 1.2. Пример использует один if let
блок для обработки Optionals, которые получаются с использование NSBundle
, NSURL
и NSData
, затем вступает в силу другой if let
блок для JSON интерпретации нескольких экземпляров AnyObject?
как специфических типов:
Нет необходимости представлять Модель User c использованием операторов функционального программирования и каррирования
В очередной раз одни прогнозировали «магическое функциональное будущее Swift», другие реализовывали его «функциональное» настоящее, а Swift 1.2 получил очень прагматичную и мощную if let конструкцию. Конечно, нужно проверить, как она себя ведет в отладке.
Продолжая тему новых возможностей Swift 1.2, которые помогают нам обрабатывать Optionals: Swift 1.2 представляет flatMap
. Это map
операция, за которой следует операция «выпрямления» (flattening).
Существующая map
позволяет вам применять функцию к значению внутри Optional, если это Optional не nil. Например, представим, что у нас есть Optional integer i
и мы хотим удвоить ее. Вы могли бы написать i.map { $0 * 2 }
. Если i
имеет значение, вы получите назад Optional удвоенного значения. С другой стороны, если i
это nil
, то дублирования не произойдет.
Теперь представим, что вместо удвоения целого числа, вам нужно выполнить map над Optional, которое само по себе возвращает Optional. В документации представлен пример с глобальной функцией find
, которая ищет в массиве заданное значение и возвращает его индекс, если значение найдено, или nil,
если оно не найдено:
Озабоченность вызывает то. что пап idx
будет Int??
, потому что fst
уже является Optional, и затем мы применяем map к find
, который также возвращает Optional – теперь у нас Optional, «завернутое» в Optional.
Мы хотим «выпрямить» (“flatten”) это вложенное Optional – поэтому вместо map мы используем
flatMap
:
А вот другое использование flatMap
: так как у нас есть Optional массив, вы могли бы использовать его для получения первого элемента от первого элемента:
first
метода для массивов:flatMap
и цепочки Optional делают одну и ту же работу. По существу, цепочки Optional — это компактная версия flatMap,
которая работает только на методах. Если вам нужна такая же функциональность для функций, в которые передается Optional значение (как find
), то нужно использовать метод flatMap
.Но это не все относительно flatMap.
Она является методом массивов (если думать о функции map для массивов, как о превращении всех элементов всех массивов в один массив элементов ), и вообще есть generic версия глобальной функции flatMap, которая работает с последовательностями (sequences) и коллекциями (collections). Есть море статей на эту тему, но лучшее объяснение с примерами дано в статьях Alexandros Salazar.
Внесены изменения в столь удобную и автоматическую взаимозаменяемость (bridging) Objective-C классов и соответствующие им Swift типы значений. Неявное преобразование из взаимозаменяемых ( bridged ) Objective-C классов (NSString/NSArray/NSDictionary) в соответствующие им Swift типы значений (String/Array/Dictionary) удалено, сделав систему типов в Swift проще и более предсказуемой, как утверждает Apple. Сюда же относится и пара NSSet в Objective-C и вновь испеченный Set в Swift. Для того, чтобы осуществить такое взаимозаменяемое преобразование, нужно выполнить это преобразование вручную с помощью операторов c ключевым словом as. Взаимозаменяемость в обратном направлении из Swift типы значений (String/Array/Dictionary) в соответствующие ( bridged ) Objective-C классы (NSString/NSArray/NSDictionary) работает автоматически.
Swift становится гражданином «первого класса»: Swift типы будут работать везде, где ожидается работа их или Objective-C аналогов. Вам придется сделать изменения в своем приложении в Swift 1.2, если вы выработаете с экземплярами старых Objective-C классов.
Автоматическая взаимозаменяемость (briging) в направлении NSString -> String не работает в Swift 1.2 автоматически и требует «кастинга»с помощью as, as?, as!
Нужно выполнить «кастинг» вручную, но будьте внимательны: автоматическое исправление ошибок будет предлагать вам as, но сигнатура метода требует as?
Автоматическая взаимозаменяемость (briging) в направлении String -> NSString сохранена и работает в Swift 1.2.
Хочу отметить, что изменилась терминология: вместо объектов (objects) в Objective-C, в Swift используются значения (values), вместо классов ( classes) в в Objective-C, в Swift используются типы (types).
Сигнатуры методов стали более адаптированы к Swift.
Например, раньше был метод
[js]
func touchesBegan(touches: NSSet, withEvent event: UIEvent)
[/js]
Теперь
[js]
func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)
[/js]
Это шаг в правильном направлении, но лучше было бы Set <UITouch>.
Новый @noescape атрибут для замыканий
Атрибут @noescape может быть использован в замыканиях для того, чтобы показать, что замыкание «не переживет» «времени жизни» вызова (call). Если более простыми словами, то замыкание с атрибутом @noescape показывает, что замыкание будет удалено (be released), когда метод закончит работу. Если вы используете этот атрибут, то вам нет необходимости при ссылке на переменные экземпляра класса использовать ключевое слово self. Это делает контекст и использование замыкания прозрачным для пользователя этого замыкания. Вдобавок, согласно Apple, это способствует оптимизации производительности.
Рассмотрим использование @noescape . Вам достаточно добавить @noescape перед именем параметра
Когда вы используете это замыкание, то вы можете осуществлять доступ к переменным экземпляра класса напрямую, как указано ниже:
Если вы пытаетесь получить доступ к переменным экземпляра класса, когда у вас нет атрибута @noescape, то компилятор даст вам ошибку, которая говорит, что в этом случае требуется явный доступ с помощью ключевого слова self.
Nullability аннотация типов аргументов, переменных и свойств в Objective-C
Nullability означает способность принимать null значения. Это соответствует концепции Optionality в Swift. Nullability может быть выражена специальными квалификаторами Objective-C типа, и имеет огромное значение для значительного количества классов в стандартной библиотеке и для сигнатуры функций.
В Swift есть четкое различие между Optional и не-Optional ссылками, например, NSView и NSView?, в то время как Objective-C представляет оба этих типа как NSView *. Так как компилятор Swift не может быть уверен наверняка, является ли данный NSView * Optional или нет, то в Swift интерпретирует этот тип как неявное «развернутое» Optional (implicitly unwrapped Optional), NSView!.
В предыдущих релизах сама Apple снабдила свои frameworks правильными Swift Optionals. Xcode 6.3 поддерживает эту возможность для вашего кода путем добавления в Objective-C новой возможности: Nullability аннотаций.
Эти изменения очень важны и обеспечивают положительное воздействие на стабильность и надежность языка, двигающегося вперед. Но это также означает, что большая часть вашего кода на Objective-С нуждается в изменениях.
Если вы продолжаете писать на Objective-C, то можете использовать некоторые новые квалификаторы для аргументов, переменных и свойств:
nonnull
– никогда не бываетnil
nullable
– может бытьnil
null_unspecified
– неизвестно, может быть nil или нет (в настоящий момент используется по умолчанию)
Рассмотрим соответствие между Objective-C декларированием и соответствующими Swift типами:
nonnull NSString *string
– обычныйString
nullable NSString *string
– OptionalString?
null_unspecified NSString *string
– неизвестно, является неявно «развернутым» ( implicitly unwrapped)String!
Рассмотрим Nullability в действии (пример взят из Swift 1.2)
Давайте посмотрим на API некоторого LocationDataСontroller, используемого для управления списком местоположений, locations. К каждому местоположению подключается фотография photo:
Без Nullability анотаций, каждый указатель в моем LocationDataController классе импортируется в Swift как неявно «развернутое» Optional (implicitly unwrapped Optional):
Вот как я могу аннотировать Objective-C API моего класса:
Во-первых, свойства — мой список местоположений locations является nonnull, так как в худшем случае, это будет пустой массив, но latestLocation может быть nil если в списке locations ничего пока нет. Параметры в моих двух методах должны всегда иметь значения, но из-за того, что не все locations имеют photo, то второй метод возвращает nullable photo.
Возвращаясь в Swift, мы видим, что результат намного лучше—теперь более понятно как безопасно можно использовать LocationDataController, и никакого раздражения:
— NSEnumerator.generate() -> NSFastGenerator
Перегрузка (overloading) функций в Swift 1.2 в Objective-C совместимых классах
Эта проблема очень подробно описана в посте «Дополнение к Лекция 2 — особенности кода Calculator в Swift 1.2 или как не «застрять» на этом месте».
Миграция на Swift 1.2
Apple обеспечила меню для перехода на версию 1.2. Это работает, но не всегда результат является подходящим.
Меню Xcode > Edit > Convert > To Swift 1.2
Есть «подводные камни», связанные с преобразованием NSSet в Set<NSObject> или NSString! в String.