Особенности наблюдателей за свойствами

Иногда бывает необходимо выполнить дополнительные операции при изменении значений свойств. Чтобы отловить события, используются наблюдатели willSet/didSet, в работе с которыми есть нюансы.

Рассмотрим пример:

import UIKit

class Object {
    var color = UIColor.greenColor() {
        didSet {
            color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
        }
    }
    
    private(set) var red = CGFloat()
    private(set) var green = CGFloat()
    private(set) var blue = CGFloat()
    private(set) var alpha = CGFloat()
    
    var point = CGPoint() {
        didSet {
            print("Point changed to \(point)")
        }
    }
    
    var name = NSMutableString() {
        didSet {
            print("Name changed to \(name)")
        }
    }

    init() {
        color = UIColor.redColor()
    }
}

Первое — наблюдатели не вызываются при задании значений по умолчанию, а также в инициализаторе:

let object = Object()
print("Red: \(object.red), green: \(object.green), blue: \(object.blue), alpha \(object.alpha)")

// Output: Red: 0.0, green: 0.0, blue: 0.0, alpha 0.0

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

object.point = CGPoint(x: 10, y: 10)
object.point.x = 20

// Output: Point changed to (10.0, 10.0)
//         Point changed to (20.0, 10.0)

object.name = NSMutableString(string: "'The new name'")
object.name.setString("Changed name")

// Output: Name changed to 'The new name'