Руководство разработчика 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 изменил правила игры для iOS-разработчиков на Objective-C.

Сравнение 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 вывести их на новые высоты.