Руководство разработчика iOS: от Objective-C до изучения Swift
Опубликовано: 2022-03-11В 2008 году Apple анонсировала и выпустила iPhone SDK 2.0. Это событие положило начало еще одной революции в разработке программного обеспечения и рождению нового поколения разработчиков. Теперь они признаны разработчиками iOS.
Многие из этих разработчиков никогда раньше не использовали Objective-C, и это был первый вызов, который бросила им Apple. Несмотря на незнакомый синтаксис и ручное управление памятью, он оказался чрезвычайно успешным, помог заполнить App Store десятками тысяч приложений. Apple постоянно совершенствовала Objective-C с каждым выпуском, добавляя блоки и литералы, упрощая управление памятью с автоматическим подсчетом ссылок и многие другие функции, свидетельствующие о современном языке программирования.
И после 6 лет совершенствования и работы над Objective-C Apple решила бросить еще один вызов разработчикам. И снова разработчикам iOS нужно будет изучить новый язык программирования: Swift . Swift удаляет небезопасное управление указателями и вводит новые мощные функции, сохраняя при этом взаимодействие как с Objective-C, так и с C.
Swift 1.0 уже является стабильной и надежной платформой для разработки, которая, несомненно, будет развиваться в ближайшие годы. Это идеальный момент, чтобы начать изучать этот новый язык, поскольку очевидно, что это будущее разработки для iOS.
Цель этого руководства — предоставить разработчикам Objective-C краткий обзор новых функций языка Swift, чтобы помочь вам сделать следующий шаг и начать внедрять Swift в своей повседневной работе. Я не буду тратить слишком много времени на объяснение Objective-C и предполагаю, что вы знакомы с разработкой для iOS.
Сравнение Swift и Objective-C
Чтобы начать изучать Swift, все, что вам нужно сделать, это загрузить XCode из App Store и создать площадку для экспериментов. Все примеры, упомянутые в этой статье, сделаны таким образом.
Домашняя страница Apple Swift — лучший справочник для изучения программирования Swift. Вы обнаружите, что это бесценно, и пока вы полностью не освоите разработку Swift, я верю, что вы будете возвращаться сюда часто.
Переменные и константы
Объявление переменной в Swift выполняется с помощью ключевого слова var
.
var x = 1 var s = "Hello"
Вы заметите, что две переменные x
и s
имеют разные типы. x
— целое число, а s
— строка. Swift — это типобезопасный язык, и он выводит типы переменных из присвоенного значения. Если вы хотите сделать свой код более читабельным, вы можете опционально аннотировать тип переменной:
var y: Int y = 2
Константы похожи, но вы объявляете их, используя let
вместо var
. Значение константы не обязательно должно быть известно во время компиляции, но вы должны присвоить ей значение ровно один раз.
let c1 = 1 // Constant known at compile time var v = arc4random() let c2 = v // Constant known only at run time
Как следует из их названия, они неизменяемы, поэтому следующий код вызовет ошибку времени компиляции.
let c = 1 c = 3 // error
Другие типы также могут быть объявлены как константы. Например, следующий код объявляет массив как константу, и если вы попытаетесь изменить любой из элементов, компилятор Swift сообщит об ошибке:
var arr2 = [4, 5, 6] arr2[0] = 8 print (arr2) // [8, 5, 6] let arr = [1, 2, 3] a[0] = 5 // error
Опции
Константы необходимо инициализировать при их объявлении, а переменные необходимо инициализировать перед использованием. Так где же nil
эквивалент Objective-C? Swift вводит необязательные значения . Необязательные значения могут иметь значение или быть nil
. Если вы посмотрите на следующий код, вы заметите, что x
было присвоено Optional
значение 2014
. Это означает, что компилятор Swift знал, что x
также может быть nil
.
var s = "2014" var x = s.toInt() print(x) // Optional(2014)
Если вы внесете изменения в этот код и присвоите s
значение "abc"
, которое не может быть преобразовано в целое число, вы заметите, что x
теперь равен nil
.
var s = "abc" var x = s.toInt() print(x) // nil
Тип возвращаемого значения функции toInt()
— Int?
, который является необязательным Int . Попробуем вызвать стандартную функцию для x
:
var x = "2014".toInt() print(x.successor()) // error
Компилятор сообщает об ошибке, так как x
является необязательным и потенциально может быть равен nil . Сначала мы должны проверить x
и убедиться, что функция- successor
вызывается для действительного числа, а не для nil
значения:
var x = "2014".toInt() if x != nil { print(x!.successor()) // 2015 }
Обратите внимание, что мы должны развернуть x
, добавив восклицательный знак (!) . Когда мы уверены, что x
содержит значение, мы можем получить к нему доступ. В противном случае мы получим ошибку времени выполнения. Мы также можем сделать то, что Swift называет необязательной привязкой , преобразовав необязательную переменную в необязательную.
let x = "123".toInt() if let y = x { print(y) }
Код в операторе if
будет выполняться только в том случае, если x
имеет значение и присваивает его y
. Обратите внимание, что нам не нужно разворачивать y
, его тип не является обязательным, поскольку мы знаем, что x
не равен nil
.
Ознакомьтесь с учебным пособием Apple Swift, чтобы узнать больше о дополнительных функциях и полезных функциях, таких как дополнительная цепочка.
Интерполяция строк
В Objective-C форматирование строк обычно выполняется с помощью метода stringWithFormat:
::
NSString *user = @"Gabriel"; int days = 3; NSString *s = [NSString stringWithFormat:@"posted by %@ (%d days ago)", user, days];
В Swift есть функция интерполяции строк , которая делает то же самое, но она более компактна и удобна для чтения:
let user = "Gabriel" let days = 3 let s = "posted by \(user) \(days) ago"
Вы также можете использовать выражения:
let width = 2 let height = 3 let s = "Area for square with sides \(width) and \(height) is \(width*height)"
Чтобы узнать больше об интерполяции строк Swift и других новых функциях, перейдите сюда.
Функции
Определение функции в Swift отличается от C. Пример определения функции приведен ниже:
func someFunction(s:String, i: Int) -> Bool { ... // code }
Функции Swift относятся к первоклассным типам . Это означает, что вы можете назначать функции переменным, передавать их в качестве параметров другим функциям или делать так, чтобы они возвращали типы:
func stringLength(s:String) -> Int { return countElements(s) } func stringValue(s:String) -> Int { if let x = s.toInt() { return x } return 0 } func doSomething(f:String -> Int, s:String) -> Int { return f(s).successor() } let f1 = stringLength let f2 = stringValue doSomething(f1, "123") // 4 doSomething(f2, "123") // 124
Опять же, Swift выводит типы f1
и f2
( String
-> Int
), хотя мы могли бы определить их явно:
let f1:String -> Int = stringLength
Функции также могут возвращать другие функции:
func compareGreaterThan(a: Int, b: Int) -> Bool { return a > b } func compareLessThan(a: Int, b: Int) -> Bool { return a < b } func comparator(greaterThan:Bool) -> (Int, Int) -> Bool { if greaterThan { return compareGreaterThan } else { return compareLessThan } } let f = comparator(true) println(f(5, 9))
Руководство по функциям в Swift можно найти здесь.
Перечисления
Перечисления в Swift намного мощнее, чем в Objective-C. Как и структуры Swift, они могут иметь методы и передаются по значению:
enum MobileDevice : String { case iPhone = "iPhone", Andro, WP8 = "Windows Phone8", BB = "BlackBerry" func name() -> String { return self.toRaw() } } let m = MobileDevice.Android print(m.name()) // "Android"
В отличие от Objective-C, перечисления Swift могут назначать строки, символы или числа с плавающей запятой в качестве значений для каждого члена, помимо целых чисел. Удобный toRaw()
возвращает значение, присвоенное каждому члену.
Перечисления также могут быть параметризованы:
enum Location { case Address(street:String, city:String) case LatLon(lat:Float, lon:Float) func description() -> String { switch self { case let .Address(street, city): return street + ", " + city case let .LatLon(lat, lon): return "(\(lat), \(lon))" } } } let loc1 = Location.Address(street: "2070 Fell St", city: "San Francisco") let loc2 = Location.LatLon(lat: 23.117, lon: 45.899) print(loc1.description()) // "2070 Fell St, San Francisco" print(loc2.description()) // "(23.117, 45.988)"
Более подробная информация о перечислениях доступна здесь.

Кортежи
Кортежи группируют несколько значений в одно составное значение. Значения в кортеже могут быть любого типа и не обязательно должны быть одного типа друг с другом.
let person = ("Gabriel", "Kirkpatrick") print(person.0) // Gabriel
Вы также можете назвать отдельные элементы кортежа:
let person = (first: "Gabriel", last: "Kirkpatrick") print(person.first)
Кортежи чрезвычайно удобны в качестве возвращаемых типов для функций, которым необходимо возвращать более одного значения:
func intDivision(a: Int, b: Int) -> (quotient: Int, remainder: Int) { return (a/b, a%b) } print(intDivision(11, 3)) // (3, 2) let result = intDivision(15, 4) print(result.remainder) // 3
В отличие от Objective-C, Swift поддерживает сопоставление с образцом в операторе switch:
let complex = (2.0, 1.1) // real and imaginary parts switch complex { case (0, 0): println("Number is zero") case (_, 0): println("Number is real") default: println("Number is imaginary") }
Во втором случае нам не нужна действительная часть числа, поэтому мы используем _
для соответствия чему угодно. Вы также можете проверить дополнительные условия в каждом случае. Для этого нам нужно привязать значения шаблона к константам:
let complex = (2.0, 1.1) switch complex { case (0, 0): println("Number is zero") case (let a, 0) where a > 0: println("Number is real and positive") case (let a, 0) where a < 0: println("Number is real and negative") case (0, let b) where b != 0: println("Number has only imaginary part") case let (a, b): println("Number is imaginary with distance \(a*a + b*b)") }
Обратите внимание, что нам нужно связать только значения, которые мы собираемся использовать в сравнении или в операторе case.
Чтобы узнать больше о кортежах, перейдите сюда.
Классы и структуры
В отличие от Objective-C, Swift не требует создания отдельных файлов интерфейса и реализации для пользовательских классов и структур. По мере изучения Swift вы научитесь определять класс или структуру в одном файле, а внешний интерфейс к этому классу или структуре автоматически становится доступным для использования другим кодом.
Определение классов
Определения классов очень просты:
class Bottle { var volume: Int = 1000 func description() -> String { return "This bottle has \(volume) ml" } } let b = Bottle() print(b.description())
Как видите, объявление и реализация находятся в одном файле . Swift больше не использует файлы заголовков и реализации. Давайте добавим метку к нашему примеру:
class Bottle { var volume: Int = 1000 var label:String func description() -> String { return "This bottle of \(label) has \(volume) ml" } }
Компилятор будет жаловаться, потому что метка является необязательным параметром и не будет содержать значение при создании экземпляра Bottle. Нам нужно добавить инициализатор:
class Bottle { var volume: Int = 1000 var label:String init(label:String) { self.label = label } func description() -> String { return "This bottle of \(label) has \(volume) ml" } }
Или мы могли бы использовать Optional
тип для свойства, которое не нужно инициализировать. В следующем примере мы сделали volume
Optional Integer
целым числом:
class Bottle { var volume: Int? var label:String init(label:String) { self.label = label } func description() -> String { if self.volume != nil { return "This bottle of \(label) has \(volume!) ml" } else { return "A bootle of \(label)" } } }
Структуры
В языке Swift тоже есть structs
, но они гораздо более гибкие, чем в Objective-C. В следующем руководстве по коду определяется struct
:
struct Seat { var row: Int var letter:String init (row: Int, letter:String) { self.row = row self.letter = letter } func description() -> String { return "\(row)-\(letter)" } }
Подобно классам в Swift, структуры могут иметь методы, свойства, инициализаторы и соответствовать протоколам. Основное различие между классами и структурами заключается в том, что классы передаются по ссылке, а структуры передаются по значению .
В этом примере демонстрируется передача классов по ссылке:
let b = Bottle() print(b.description()) // "b" bottle has 1000 ml var b2 = b b.volume = 750 print(b2.description()) // "b" and "b2" bottles have 750 ml
Если мы попробуем аналогичный случай со struct
, вы заметите, что переменные передаются по значению:
var s1 = Seat(row: 14, letter:"A") var s2 = s1 s1.letter = "B" print(s1.description()) // 14-B print(s2.description()) // 14-A
Когда мы должны использовать struct
и когда мы должны использовать class
? Как и в Objective-C и C, используйте структуры, когда вам нужно сгруппировать несколько значений и ожидать, что они будут копироваться, а не ссылаться на них. Например, комплексные числа, 2D- или 3D-точки или цвета RGB.
Экземпляр класса традиционно известен как объект. Однако классы и структуры Swift гораздо ближе по функциональности, чем в других языках, и многие функции могут применяться к экземплярам класса или типа структуры. Из-за этого более общий термин, используемый в справочнике Swift, — instance
, который применяется к любому из этих двух.
Изучите основы классов и структур Swift здесь.
Характеристики
Как мы видели ранее, свойства в Swift объявляются с помощью ключевого слова var
внутри определения класса или структуры. Мы также можем объявлять константы с помощью оператора let
.
struct FixedPointNumber { var digits: Int let decimals: Int } var n = FixedPointNumber(digits: 12345, decimals: 2) n.digits = 4567 // ok n.decimals = 3 // error, decimals is a constant
Также имейте в виду, что на свойства класса строго ссылаются, если вы не ставите перед ними ключевое слово weak
. Однако есть некоторые тонкости со слабыми необязательными свойствами, поэтому прочитайте главу об автоматическом подсчете ссылок в руководстве Apple Swift.
Вычисленные свойства
Вычисляемые свойства фактически не хранят значения. Вместо этого они предоставляют геттер и необязательный сеттер для косвенного извлечения и установки других свойств и значений.
В следующем коде представлен пример sign
вычисляемого значения:
enum Sign { case Positive case Negative } struct SomeNumber { var number:Int var sign:Sign { get { if number < 0 { return Sign.Negative } else { return Sign.Positive } } set (newSign) { if (newSign == Sign.Negative) { self.number = -abs(self.number) } else { self.number = abs(self.number) } } } }
Мы также можем определить свойства только для чтения, просто реализовав геттер:
struct SomeNumber { var number:Int var isEven:Bool { get { return number % 2 == 0 } } }
В Objective-C свойства обычно поддерживаются переменной экземпляра, объявленной явно или автоматически созданной компилятором. С другой стороны, в Swift свойство не имеет соответствующей переменной экземпляра . То есть к резервному хранилищу свойства нельзя получить прямой доступ. Предположим, у нас есть это в Objective-C
// .h @interface OnlyInitialString : NSObject @property(strong) NSString *string; @end // .m @implementation OnlyInitialString - (void)setString:(NSString *newString) { if (newString.length > 0) { _string = [newString substringToIndex:1]; } else { _string = @""; } } @end
Поскольку в Swift вычисляемые свойства не имеют резервного хранилища, нам нужно сделать что-то вроде этого:
class OnlyInitialString { var initial:String = "" var string:String { set (newString) { if countElements(newString) > 0 { self.initial = newString.substringToIndex(advance(newString.startIndex, 1)) } else { self.initial = "" } } get { return self.initial } } }
Свойства описаны более подробно здесь
Продолжение следует
В Swift есть много других важных новых вещей, таких как дженерики, взаимодействие с библиотеками Objective-C, замыкания, необязательная цепочка и перегрузка операторов. Одно руководство не может полностью описать новый язык, но я не сомневаюсь, что об изучении программирования на Swift будет написано гораздо больше. Тем не менее, я считаю, что это краткое чтение поможет многим разработчикам Objective-C, которым не удалось найти время и изучить детали языка Swift, встать на правильный путь и позволить птичке Swift вывести их на новые высоты.