Изменения в Swift 2.2

Совсем недавно вышел Swift 2.2, внесший ряд изменений в язык.

Наиболее значимые среди них:

С полным списком обновлений можно ознакомиться на официальном сайте языка.

Кортежи

Далеко не все знают, что Swift с самого выхода поддерживает особый вид списочных типов — кортежи. Их отличительной чертой является способность хранить вместе элементы различных типов (ни массивы, ни словари этого не могут).

Обобщая, кортеж представляет собой упорядоченный, фиксированный по длине набор элементов, обращение к которым возможно по порядковым индексам или именам, если они заданы.

import Foundation

typealias Tuple = (id: Int, name: String)

func tupleFromId(id: Int, name: String) -> Tuple {
    return (id, name)
}

let tuple = tupleFromId(1, name: "Swift")

print("Tuple id: \(tuple.id), name: \(tuple.name)")
print("Tuple id: \(tuple.0), name: \(tuple.1)")

Генерация последовательностей

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

Метод stride принимает на вход граничное значение последовательности и шаг. А вариант его вызова с to или through определяет вхождение самой границы (если на ней останавливается расчет) в конечный результат (to — не войдет, through — войдет).

import Foundation

for i in 1.stride(to: 25, by: 3) {
    // 1, 4, 7, 10, 13, 16, 19, 22
    // 25 excluded
}

for j in 1.stride(through: 25, by: 3) {
    // 1, 4, 7, 10, 13, 16, 19, 22, 25
    // 25 included
}

for k in 6.stride(through: 1, by: -2) {
    // 6, 4, 2
}

Замена пустых значений

Если в логике требуется взять значение по умолчанию вместо пустого, можно воспользоваться тернарным выражением. Но наиболее изящным будет применение специального оператора «??».

import Foundation

let dividend: Double = 222
let divisor: Double? = nil

dividend / (divisor == nil ? 1 : divisor!)
dividend / (divisor ?? 1)

Виды типов

В языке Swift типы делятся на две группы: типы значений и ссылочные.

К типам значений относятся перечисления и структуры (например, стандартные строки, числа, словари и массивы относятся к ним). Переменные с таким типом содержат непосредственно само значение, а переприсвоение между ними происходит через полное копирование этого значения.

import Foundation

var firstValue: Double = 11
var secondValue: Double = firstValue

secondValue += 1

print(firstValue)
// 11

print(secondValue)
// 12

Ссылочными типами являются классы и замыкания (они же блоки). Такие переменные хранят только ссылку на объект, но не его сам, а копирование производится путем создания еще одной ссылки.

import UIKit

var firstView: UIView = UIView()
var secondView: UIView = firstView

print(firstView.tag)
//0

secondView.tag = 10

print(secondView.tag)
// 10

print(firstView.tag)
// 10

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

Ссылочные же типы наоборот, могут создать трудности в многопоточных приложениях, но позволяют экономить память, держа в ней лишь небольшое число общих экземпляров. Однако, чтобы при этом не столкнуться с утечкой памяти, нужно понимать механизм ее управления и очистки. В Swift за это отвечает Automatic Reference Counting (ARC), подсчитывающий количество ссылок на каждый объект. И только при снижении этого показателя до нуля объект деинициализируется и удаляется из памяти.

В большинстве случаев ARC прекрасно справляется со своей работой самостоятельно. За исключением зацикливаний, когда один объект имеет ссылку на другой, а тот на него. В этой ситуации разработчику необходимо сделать одну из ссылок «слабой» (не учитываемой ARC), превратив связь в одностороннюю. Для этого служат ключевые слова weak (с необязательными переменными) и unowned (с обязательными).

import Foundation

class ControllerPart {
    var controller: Controller?

    init(controller: Controller) {
        self.controller = controller
    }
}

class Controller {
    var part: ControllerPart?
    
    deinit {
        print("Controller deleted")
    }
}

var controller: Controller? = Controller()
var controllerPart: ControllerPart? = ControllerPart(controller: controller!)

controller!.part = controllerPart!

controller = nil
/* 
  Controller not deleted, because controllerPart still has a reference to it.
  But if we make this reference weak:

    weak var controller: Controller?
    or
    unowned var controller: Controller

  Controller deleted
*/

У замыканий это делается в списке захвата.

import Foundation

class Mechanism {
    var strength = 0
    lazy var action: () -> Void = {
        [unowned self] in
        self.strength++
    }

    deinit {
        print("Mechanism deleted")
    }
}

var mechanism: Mechanism? = Mechanism()

// Make lazy property active
mechanism?.action()

mechanism = nil
// Mechanism deleted

Дополнительные материалы: