Что нового в Swift 1.2

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

В очередной понедельник, когда мы думали что Apple усиленно работает над WatchKit и Xcode 6.2 beta, мы вдруг получили известие о выходе Xcode 6.3 и Swift 1.2!

В Swift 1.2 привнесено множество прекрасных изменений! Но не стоит забывать о том, что Swift 1.2 все еще beta. Так что не будем забегать вперед и переделывать все свои приложения на него.

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

  • Улучшение в скоростях
  • Улучшение в опциональной привязке if-let
  • Изменения в операторе приведения as
  • Нативные сеты Swift
  • Изменения в инициализирующих константах
  • Улучшено взаимодействие и бриджинг Objective-C

Для того чтобы все связать воедино, мы воспользуемся новым инструментом Swift - «Migrator». И за одно посмотрим насколько хорошо этот инструмент конвертирует проекты, написанные на Swift 1.1 до Swift 1.2.

Итак, главные изменения в Swift 1.2.

Улучшения в скоростях

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

Swift уже стал быстрее, чем Objective-C в некоторых случаях и приятно наблюдать, что над его производительностью продолжают работать!

Улучшения в опциональной привязке if-let

Точно названная "пирамида смерти" больше не помеха! До Swift 1.2 вы могли записать множественную проверку опциональных значений только в таком виде:

// До Swift 1.2
if let data = widget.dataStream {
  if data.isValid {
    if let source = sourceIP() {
      if let dest = destIP() {
        // что-то делаем
      }
    }
  }
}

Но теперь, вы можете сделать это вот так:

// В Swift 1.2
if let data = widget.data where data.isValid, let source = sourceIP(), dest = destIP() {
  // что-то делаем
}

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

Повышающее и понижающее приведение

Ключевое слово as использовалось и при понижающем приведении, и при повышающем:

// до Swift 1.2
var aView: UIView = someView()
 
var object = aView as NSObject // повышающее приведение
 
var specificView = aView as UITableView // понижающее приведение

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

Однако, понижающее приведение все же может и зафейлится, так как никогда мы не можем знать наверняка, есть ли класс при понижении. Например, если у нас есть UIView, то он может быть как UITableView, так и UIButton. Если ваше понижающее приведение пойдет к правильному типу - здорово! Но если так случится, что будет указан неверный тип, вы получите runtime ошибку и ваше приложение упадет.

В Swift 1.2 понижающее приведение должно быть либо опциональной формы as?, либо принуждающей формы as!. Если вы уверены насчет типа, то вы можете использовать принуждающую форму, это аналогично тому, как вы используете восклицательный знак в принудительно-извлекаемых опциональных значениях:

// в Swift 1.2
var aView: UIView = someView()
 
var tableView = aView as! UITableView

Восклицательный знак показывает, что вы абсолютно уверены насчет того, что делаете, но существует вероятность того, что все выйдет из-под контроля, если вы случайно перепутаете типы!

Ну и как всегда as? вместе с опциональной привязкой является самым безопасным способом проверки типа:

// Это не новшество Swift 1.2, но все еще самый безопасный способ
var aView: UIView = someView()
 
if let tableView = aView as? UITableView {
  // что-то делаем с tableView
}

Сеты

Swift 1.0 изначально имел строки, массивы и словари, которые корнями уходили в NSString, NSArray, NSDictionary. Любители структур данных спрашивали: "А куда подевались сеты?"

Лучше поздно, чем никогда - теперь нативные сеты Swift на месте! Также на ряду с массивами и словарями мы имеем новый тип Set, который является структурой с семантикой значения.

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

Если вы ранее использовали NSSet, то вы почувствуете себя как "рыба в воде":

var dogs = Set<String<()
 
dogs.insert("Shiba Inu")
dogs.insert("Doge")
dogs.insert("Husky puppy")
dogs.insert("Schnoodle")
dogs.insert("Doge")
 
// prints "Существует 4 известные породы собак"
println("Существует \(dogs.count) известные породы собак")

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

Это отличное дополнение к стандартной библиотеке, которое заполняет огромную дыру абстракции, в которой вам приходилось опускаться до NSSet. Будем надеяться, что другие недостающие типы, например NSDate, будут следующими в списке апгрейда Swift.

Константы let

Константы - это безопасность и гарантия того, что вещи которые не должны меняться, не будут изменены. Мы руководствуемся правилом: "Определяй везде константы и исправляй их на переменные, только в случае ошибки компиляции!"

Самая большая проблема констант - начальные значения. Были некоторые обходные пути, начиная с var и заканчивая замыканием, в котором мы присваивали значение.

Но в Swift 1.2 вы можете определять константы let и иногда указывать их значения позже. Конечно, вы обязаны указать их значения до обращения к ним, но теперь вы можете устанавливать значения через условия, например:

let longEdge: CGFloat
 
if isLandscape {
  longEdge = image.calculateWidth()
} else {
  longEdge = image.calculateHeight()
}
 
// вы можете обращаться к longEdge, начиная отсюда

Теперь использовать let стало значительно удобнее с меньшим количеством побочных эффектов и ваш код станет еще чище и безопаснее!

Взаимодействие с Objective-C

По мере роста Swift дефолтные классы будут медленно, но верно приближаться к родным классам Swift, что собственно уже и происходит!

В Swift 1.2 классы Objective-C, которые имеют эквивалент Swift (NSString, NSArray, NSDictionary...) больше автоматически не связываются между собой! А это значит, что передавая NSString в функцию, которая ожидает String, вы получите ошибку!

func mangleString(input: String) {
  // что-то делаем со входными параметрами
}
 
let someString: NSString = "hello"
 
mangleString(someString) // ошибка компиляции!

Если вы хотите сделать это, то вам нужно указать конвертацию в явной форме:

mangleString(someString as String)

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

Опционалы

Если вы следили за развитием Swift вместе с нами, то наверняка заметили, что аргументы менялись от UIView к UIView!, или UIView?, и обратно к UIView. Мы были избалованы тем, что в Objective-C у нас была возможность отправлять nil сообщения, но Swift намного строже насчет этого.

Если вы поддерживаете код Objective-C, то у вас есть новые классификаторы на вооружение, когда вы указываете тип для аргументов, переменных, свойств и т.д.:

  • nonnull - никогда не будет nil
  • nullable - может быть nil
  • null_unspecified - не известно, может ли он быть nil или нет (стоит по умолчанию)

Например, давайте посмотрим как соотносятся эти Objective-C объявления со Swift:

  • nonnull NSString *string - обычный объект, string
  • nullable NSString *string - является опциональным, string?
  • null_unspecified NSString *string - неизвестно, таким образом принудиельно развертывается, string!

Если вы не поддерживаете код Objective-C, то вы все еще можете пользоваться этими новыми классификаторами в заголовках Cocoa.

Swift migrator

Xcode 6.3 beta включает в себя Swift-migrator, для помощи переноса вашего кода на версию Swift 1.2

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

Swift-migrator доступен из Edit\ConvertTo Swift 1.2.... Сначала вы выбираете цель - это очень похоже на то, как работает рефакторинг - Xcode выходит и снова возвращаться в превью. Затем вы увидите старый код и новый код, рядом с указанными изменениями.

В этом проекте вся автоматическая миграция заключалась в изменении as на as!. И был еще оператор, замещающий nil, и  тогда «Migrator» нас не понял:

let date = (data["metadata"] as NSDictionary?)?["lastUpdated"] as? NSDate ?? beginningOfTimeDate

Это достаточно сложное выражение! «Migrator» немного подумал и предположил, что здесь записаны два выражения. Решение? - конечно, точка с запятой!

let date = (data["metadata"] as! NSDictionary?)?["lastUpdated"] as? NSDate ??; beginningOfTimeDate

Некоторые такие синтаксические "ошибки" не компилируются. А остальные дополнения насчет as! имеют смысл (и отображают мою уверенность в понижающем приведении!), но вот эта одна строка должна была быть изменена вручную.

Что дальше?

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

Урок подготовил: Иван Акулов

Источник урока: http://www.raywenderlich.com/95181/whats-new-in-swift-1-2