Введение
Swift — это новый язык программирования для разработки приложений для iOS и OS X. Он основан на C и Objective-C, но без ограничений совместимости C. Swift применяет безопасные шаблоны программирования и добавляет современные возможности, делая программирование проще, гибче и увлекательнее. Интерфейс построен на широко любимых фреймворках Cocoa и Cocoa Touch, демонстрируя новое направление в разработке программного обеспечения.

Swift разрабатывался много лет. Apple использовала существующие компиляторы, отладчики и фреймворки в качестве базовой архитектуры. Он использует ARC (автоматический подсчёт ссылок) для упрощения управления памятью. Наш стек фреймворков всегда был основан на Cocoa. Objective-C развивался, чтобы поддерживать блоки, литералы коллекций и модули, позволяя использовать современные языковые фреймворки без глубокого вмешательства. Благодаря этой основополагающей работе стало возможным внедрить новый язык программирования в разработку программного обеспечения Apple.
Разработчикам на Objective-C Swift покажется знакомым. Swift перенимает именованные параметры Objective-C и динамическую объектную модель. Он обеспечивает совместимость с фреймворками Cocoa и смешанное использование. Опираясь на эти основы, Swift вводит множество новых возможностей и объединяет процедурную и объектно-ориентированную функциональность.
Swift также дружелюбен к новым программистам. Это язык системного программирования промышленного качества, но при этом такой же дружелюбный, как скриптовый язык. Он поддерживает playgrounds, позволяя программистам экспериментировать с фрагментом кода Swift и сразу видеть результат, без хлопот со сборкой и запуском приложения.
Swift объединяет идеи из современных языков программирования и мудрость инженерной культуры Apple. Компилятор оптимизирован для производительности, а язык — для разработки, не жертвуя ни тем, ни другим. Вы можете начать с «Hello, world» и перейти к целой системе. Всё это делает Swift источником инноваций для разработчиков программного обеспечения Apple.
Swift — фантастический способ писать приложения для iOS и OS X, и в него будут и дальше добавляться новые возможности. Мы с нетерпением ждём, что вы создадите с его помощью.
Начало работы со Swift
Изучение нового языка следует начинать с вывода «Hello, world». В Swift это всего одна строка:
println("Hello, world")
Если вы писали код на C или Objective-C, этот синтаксис покажется знакомым. В Swift это законченная программа. Не нужно импортировать отдельную библиотеку для ввода-вывода и обработки строк. Код на глобальном уровне является точкой входа в программу, поэтому не нужно писать функцию main(). Также не нужно ставить точку с запятой после каждого оператора. Этот вводный материал даёт достаточно информации, чтобы научить вас выполнять задачу программирования. Не беспокойтесь о том, чего пока не понимаете; всё, что здесь объяснено недостаточно ясно, будет подробно рассмотрено позже в этой книге.
Примечание:В качестве лучшей практики вы можете открыть эту главу в Xcode playground. Playgrounds позволяют редактировать код и сразу видеть результаты.
Простые значения
Используйте let для определения константы и var для определения переменной. Значение константы не обязательно должно быть известно во время компиляции, но вы должны присвоить его хотя бы один раз. Это означает, что вы можете использовать константу, чтобы дать имя значению, которое вы определяете один раз, но используете во многих местах.
var myVariable = 42 myVariable = 50 let myConstant = 42
Примечание:Определения констант здесь похожи на переменные в функциональных языках программирования: после присваивания их нельзя изменить. Используйте их часто; это пойдёт вам на пользу.
Константа или переменная должна иметь тот же тип, что и присваиваемое значение. Поэтому вам не нужно явно определять тип. Просто укажите значение, чтобы создать константу или переменную, и позвольте компилятору вывести её тип. В примере выше компилятор выводит, что myVariable имеет целочисленный тип, потому что её начальное значение является целым числом.
Примечание:Типы привязаны к именам переменных, что делает этот язык статически типизированным. Это помогает статической оптимизации. Это отличается от Python, JavaScript и других. Если начальное значение не даёт достаточно информации (или если начального значения нет), вы можете написать тип после имени переменной, разделив их двоеточием.
let imlicitInteger = 70 let imlicitDouble = 70.0 let explicitDouble: Double = 70
Значения никогда не преобразуются неявно в другие типы. Если вам нужно преобразовать значение в другой тип, явно создайте экземпляр нужного типа.
let label = "The width is " let width = 94 let widthLabel = label + String(width)
Есть ещё более простой способ включать значения в строки: напишите значение в круглых скобках и поставьте обратную косую черту ("\") перед скобками. Например:
let apples = 3 let oranges = 5 let appleSummary = "I have \(apples) apples." let fruitSummary = "I have \(apples + oranges) pieces of fruit."
Чтобы создать массив или словарь, используйте квадратные скобки "[]", а доступ к их элементам осуществляйте, записывая индекс или ключ в скобках.
var shoppingList = ["catfish", "water", "tulips", "blue paint"] shoppingList[1] = "bottle of water" var occupations = [ "Malcolm": "Captain", "Kaylee": "Mechanic", ] occupations["Jayne"] = "Public Relations"
Чтобы создать пустой массив или словарь, используйте синтаксис инициализатора:
let emptyArray = String[]() let emptyDictionary = Dictionary<String, Float>()
Если информацию о типе нельзя вывести, вы можете записать пустой массив как "[]", а пустой словарь как "[:]" — например, когда вы задаёте переменную известного типа и передаёте её как аргумент в функцию.
shoppingList = [] //去购物并买些东西
Управление потоком
Используйте if и switch для условных конструкций. Используйте for-in, for, while и do-while для циклов. Круглые скобки не обязательны, но фигурные скобки вокруг тела обязательны.
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScores += 3
} else {
teamScores += 1
}
}
teamScore
В операторе if условие должно быть логическим выражением — это означает, что if score { ... } является ошибкой и не может неявно сравниваться с 0. Вы можете использовать if и let вместе для работы со значениями, которые могут отсутствовать. Такие значения являются опциональными. Опциональное значение может содержать значение или содержать nil, чтобы указать, что значение ещё не существует. Напишите знак вопроса "?" после типа, чтобы пометить значение как опциональное.
var optionalString: String? = "Hello"
optionalString == nil
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Примечание:Если опциональное значение равно nil, условие ложно, и код в фигурных скобках пропускается. В противном случае опциональное значение разворачивается и присваивается константе, делая развёрнутое значение доступным внутри блока кода.
switch поддерживает многие виды данных и широкий спектр сравнений — он не ограничивается целыми числами и проверками на равенство.
let vegetable = "red pepper"
switch vegetable {
case "celery":
let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
let vegetableComment = "Is it a spicy \(x)?"
default:
let vegetableComment = "Everything tastes good in soup."
}
Примечание:
После выполнения совпавшего case программа выходит из switch, а не переходит к следующему case. Поэтому break больше не нужен для выхода из switch.
Вы можете использовать for-in для перебора каждого элемента в словаре, указав пару имён для использования для каждой пары ключ-значение.
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
Используйте while для повторения блока кода, пока условие не изменится. Условие цикла можно разместить в конце, чтобы гарантировать, что цикл выполнится хотя бы один раз.
var n = 2
while n < 100 {
n = n * 2
}
n
var m = 2
do {
m = m * 2
} while m < 100
m
Вы можете вести индекс в цикле, используя ".." для указания диапазона индексов, или явно объявляя начальное значение, условие и шаг. Эти два цикла делают одно и то же:
var firstForLoop = 0
for i in 0..3 {
firstForLoop += i
}
firstForLoop
var secondForLoop = 0
for var i = 0; i < 3; ++i {
secondForLoop += 1
}
secondForLoop
Диапазон, созданный с помощью .., игнорирует наибольшее значение, тогда как диапазон, созданный с помощью ..., включает оба значения.
Функции и замыкания
Используйте func для объявления функции. Вызывайте функцию по её имени, за которым следует список аргументов в круглых скобках. Используйте -> для разделения имён параметров и типа возвращаемого значения.
func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")
Используйте кортеж для возврата нескольких значений.
func getGasPrices() -> (Double, Double, Double) {
return (3.59, 3.69, 3.79)
}
getGasPrices()
Функция может принимать переменное число аргументов, которые собираются в массив.
func sumOf(numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
sumOf()
sumOf(42, 597, 12)
Функции могут быть вложенными. Вложенная функция может обращаться к переменным функции, в которой она определена. Вы можете использовать вложенные функции, чтобы организовать код, который иначе был бы слишком длинным или сложным.
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()
Функции — это типы первого класса. Это значит, что функция может возвращать другую функцию.
func makeIncrementer() -> (Int -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
Функция может принимать другие функции в качестве аргументов.
func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)
Функции на самом деле являются частным случаем замыканий. Вы можете написать замыкание без имени, просто заключив его в фигурные скобки. Используйте in, чтобы отделить аргументы и возвращаемое значение от тела.
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
При написании замыканий есть несколько возможностей. Когда тип замыкания уже известен, например для обратного вызова, вы можете опустить его параметры и тип возвращаемого значения или и то, и другое. Замыкание из одного выражения может напрямую возвращать своё значение.
numbers.map({number in 3 * number})
Вы можете ссылаться на аргумент по номеру, а не по имени, что полезно для очень коротких замыканий. Замыкание, переданное как последний аргумент функции, можно разместить после круглых скобок вызова функции.
sort([1, 5, 3, 12, 2]) { $0 > $1 }
Объекты и классы
Используйте class для создания класса. Объявление свойства — это просто объявление константы или переменной в контексте класса. Методы и функции записываются одинаково.
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
Создайте экземпляр класса, поставив круглые скобки после имени класса. Используйте точечный синтаксис для доступа к свойствам и методам экземпляра.
var shape = Shape() shape.numberOfSides = 7 var shapeDescription = shape.simpleDescription()
В этой версии класса Shape отсутствует кое-что важное: инициализатор для настройки класса при создании экземпляра. Используйте init, чтобы создать его.
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A Shape with \(numberOfSides) sides."
}
}
Примечание:self используется, чтобы отличить свойство name от аргумента name. Инициализаторы объявляются как функции, за исключением того, что они создают экземпляр класса. Каждому свойству нужно присвоенное значение: либо в его объявлении, либо в инициализаторе.
Используйте deinit, чтобы создать деинициализатор для выполнения очистки, когда объект уничтожается.
Подклассы включают имя своего суперкласса, разделённое двоеточием. Если наследование происходит от стандартного корневого класса, объявлять суперкласс не нужно, поэтому его можно опустить.
Методы в подклассе могут переопределять реализацию суперкласса, если пометить их override. Без override компилятор считает это ошибкой. Компилятор также проверяет, что методы, помеченные override, действительно переопределяют метод суперкласса.
class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
Примечание:Свойства могут иметь геттеры и сеттеры.
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength
В сеттере для perimeter новое значение имеет неявное имя newValue. Если хотите, можно указать другое имя в скобках после set.
Примечание:Инициализатор EquilateralTriangle содержит три разных шага:
- Установка значения свойств
- Вызов инициализатора суперкласса
- Изменение значения свойства, определённого суперклассом, и добавление дополнительной работы, чтобы здесь также использовать методы, геттеры и сеттеры.
Если вычисляемое свойство не нужно, но всё же требуется выполнить работу после установки значения, используйте willSet и didSet. Например, следующий класс гарантирует, что длина стороны его треугольника равна длине стороны прямоугольника.
class TriangleAndSquare {
var triangle: EquilaterTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilaterTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength
Есть важное различие между методами класса и функциями. Имена параметров функции используются только внутри функции, но имена параметров метода также можно использовать при вызове метода (кроме первого параметра). По умолчанию метод имеет параметр с тем же именем, и при вызове используется сам параметр. Можно указать второе имя, которое будет использоваться внутри метода.
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes times: Int) {
count += amount * times
}
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)
При работе с опциональными значениями можно написать "?" перед оператором, подобно методам и свойствам. Если значение перед "?" уже равно nil, всё после "?" автоматически игнорируется, и всё выражение равно nil. В противном случае опциональное значение разворачивается, и всё после "?" действует на развёрнутое значение. В обоих случаях значением всего выражения является опциональное значение.
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") let sideLength = optionalSquare?.sideLength
Перечисления и структуры
Используйте enum для создания перечисления. Как классы и другие именованные типы, перечисления могут иметь методы.
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescrition() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.toRaw())
}
}
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw()
В приведённом выше примере тип исходного значения — Int, поэтому достаточно указать только первое исходное значение. Последующие исходные значения присваиваются по порядку. Вы также можете использовать строки или числа с плавающей точкой в качестве исходных значений перечисления.
Используйте функции toRaw и fromRaw для преобразования между исходными значениями и значениями перечисления.
if let convertedRank = Rank.fromRaw(3) {
let threeDescription = convertedRank.simpleDescription()
}
Значения членов перечисления — это фактические значения, а не исходные значения, записанные каким-то другим способом. На самом деле в некоторых случаях исходного значения нет, то есть когда вы его не задаёте.
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "dismonds"
case .Clubs:
return "clubs"
}
}
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
Обратите внимание на два способа ссылки на член Hearts выше: при присваивании константе hearts член перечисления Suit.Hearts указывается полным именем, потому что у константы нет явного типа. В switch перечисление указывается через .Hearts, потому что значение self уже известно. Вы можете использовать удобный способ всякий раз, когда это возможно.
Используйте struct для создания структуры. Структуры поддерживают многие из тех же возможностей, что и классы, включая методы и конструкторы. Одно важное отличие состоит в том, что структуры всегда копируются при передаче в коде (передача по значению), тогда как классы передаются по ссылке.
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \
(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
<p><strong>注意:</strong>
Член экземпляра enum может иметь значения экземпляра. Экземпляры одного и того же члена enum могут иметь разные значения. Вы присваиваете значения при создании экземпляра. Разница между присвоенными значениями и исходными значениями: исходные значения enum одинаковы для всех его экземпляров, а исходные значения указываются при определении enum.
Например, предположим, что в некоторой ситуации требуется получить время восхода и заката с сервера. Сервер может ответить той же информацией или каким-либо сообщением об ошибке.
enum ServerResponse {
case Result(String, String)
case Error(String)
}
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
Примечание:Время восхода и заката фактически выбирается из частичного совпадения ServerResponse.
Ссылки
Учебник по началу работы с языком программирования Apple Swift:http://gashero.iteye.com/blog/2075324
Язык программирования Swift:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/