Используйте метод deinit для создания деинициализатора, если нужно перед уничтожением объекта выполнить какую-то очистку.
Классы могут наследоваться. Наследующие классы включают имя суперкласса (их родителя) после своего имени. При этом, классу не обязательно наследоваться от какого-то другого класса (в отличие от Objective-C, где нужно было наследоваться от NSObject - примечание переводчика), в этих случаях имя родителя можно пропустить.
Класс-наследник может определять методы своего родителя, используя слово 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 \(sidesLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
В дополнение к простым хранимым свойствам, свойства могут также иметь getter и setter методы, т.е. методы, используемые для получения и установки их значения.
Например:
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.
Обратите внимание, что инициализатор для EqualiateralTriangle имеет три разных шага:
- Устанавливает значение свойств, объявляемых подклассом
- Вызывает инициализатор класса-предка
- Изменяет значение свойств, объявленных предком.
Любые дополнитеьлные действия так же можно сделать на последнем шаге.
Если вам не нужно вычислять свойство, но по прежнему нужно предоставить код, который будет запущен до и после получения нового значения, используйте willSet и didSet. Например, класс ниже проверяет, что длина стороны треугольника всегда такая же, как длина стороны квадрата:
class TriangleAndSquare {
var triangle: EquilateralTriangle {
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 = EquilateralTriangle(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
Перечисления (enumerations) и структуры
enum используется для создания перечисления. Как классы и другие именованные типы, перечисления могут иметь ассоциированные с ними методы:
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eigth, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> 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, поэтому мы указали только первое - Ace равное 1. Остальные были добавлены по очереди (2, 3, 4 и т.д.). Можно также использовать строки или дробные числа в качестве исходных значений перечисления.
Используйте 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 "hears"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
Обратите внимание на то, как мы используем Hearts в двух разных случаях: когда присваиваем значение константе hearts, мы используем полное имя Suit.Hearts, т.к. константа не имеет определенного типа. Внутри switch'a, мы используем .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()
Объект перечисления может иметь значения, ассоциирующиеся с объектом. Объекты одного и того же члена перечисления могут иметь разные значения, ассоциированные с ними. Вы предоставляете ассоциированные значения, когда создаете объект. Ассоциированные значения и исходные значения различны: исходное значение члена перечисления одинаковы для всех его объектов, вы задаете его при определении перечисления. Например, рассмотрим случай запроса времени заката и рассвета с сервера. Сервер отвечает или информацией, или ошибкой.
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 как пара значений, соответствующих случаям case.
Протоколы и расширения
Используйте слово protocol для объявления протокола.
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating fund adjust()
}
Классы, перечисления и структуры могут соответствовать протоколам:
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 66923
fund adjust() {
simpleDescription += " Now 100% adjusted."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating fund adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
Обратите внимание на ключевое слово mutating, которое обозначает метод, модифицирующий структуру. Объявление класса не требует добавления слова mutating, т.к. методы класса всегда могут модифицировать класс.
Используйте слово extension (расширение) для добавления функциональности существующему типу, например новых методов или вычисленных свойств. Вы можете использовать расширение для добавления совместимости с протоколом типу, который объявлен в другом месте, или даже типу, который вы импортировали из библиотеки или фреймворка:
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
simpleDescription
Вы можете использовать имя протокола, как любой другой именованный тип - например, создать коллекцию объектов, которые имеют разные типы, но все соответствуют одному протоколу. Когда вы работаете со значениями, тип которых - протокол, методы вне объявления протокола недоступны.
let protocolValue: ExampleProtocol = a
protocolValue.simpleDescription
// protocolValue.anotherProperty
Хотя переменная protocolValue будет иметь тип SimpleClass во время исполнения, компилятор работает с ней как с переменной типа ExampleProtocol - это значит, что вы не сможете случайно обратиться к методам или свойствам, которые класс реализует в дополнение к протоколу.
Общие функции и типы
Напишите имя внутри <>, чтобы создать общую функцию или тип:
func repeat<ItemType>(item : ItemType, times: Int) ->
ItemType[] {
var result = ItemType[]()
for i in 0..times {
result += item
}
return result
}
repeat("knock", 4)
Вы можете создать общие формы функций и методов, так же как и классов, перечислений и структур.
// Переопределяем опциональный тип из стандартной библиотеки Swift:
enum OptionalValue<T> {
case None
case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)
Используйте where после имени типа, чтобы указать список требований - например, потребовать, чтобы тип реализовывал протокол, потребовать чтобы два типа были одинаковы или потребовать, чтобы класс имел определенный суперкласс:
func anyCommonElements <T, U where T: Sequence, U: Sequence,
T.GeneratorType.Element: Equatable, T.GeneratorType.Element == U.GeneratorType.Element > (lhs: t, rhs: U) -> Bool {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1, 2, 3], [3])
В простых случаях, where можно пропустить и просто написать имя класса или протокола после двоеточия. Т.е. написать <T: Equatable> - это то же самое, что и <T where T: Equatable>.
Дополнительно
Facebook Parse будет использовать новый язык программирования Apple Swift
«Здесь, в Parse, мы очень рады появлению Swift, потому что язык приносит массу новых функций для разработчиков iOS и OS X,— отметил в официальном блоге программист компании Форко Маротто (Fosco Marotto). — Интерфейс классов Swift позволит разработчиков сэкономить массу времени на создании кода. И в целом снизит число ошибок времени исполнения…».
Крупнейшая социальная сеть в мире Facebook поглотила Parse в апреле 2013 года с целью усилить свои позиции в мире мобильных приложений и платных служб «бизнес для бизнеса».
Стоит отметить, что Swift совместим с существующими библиотеками Objective-C, включая сторонние вроде Parse, так что интеграция языка не должна стать большой проблемой. Подробнее о том, как использовать каркас приложений Parse в проекте Swift, можно узнать в детальной инструкции Stackoverflow.