禁止直接分配的可变属性-Swift

时间:2019-05-25 02:43:50

标签: swift assignment-operator value-type mutability mutating-function

排除分配中的可变可变性

Swift 5.0-Xcode 10.2.1

我有一个类,该类具有一个我想可变的属性,除了我不想允许使用=运算符直接分配该属性。请看以下示例:

class Foo {
    var counter = Counter()

    init() { }
}

struct Counter {
    private(set) var n = 0

    let interval: Int

    mutating func increment() {
        n += interval
    }

    init(by interval: Int = 1) {
        self.interval = interval
    }
}

我想被允许的内容:

let foo = Foo()
foo.counter.increment()

我不希望被允许的内容:

let fasterCounter = Counter(by: 10)
let foo = Foo()
foo.counter = fasterCounter

注意: 虽然我知道可以将counter设为private(set) var,并在incrementCounter()中创建并创建一个Foo函数以使其递增,但我希望能够访问该变异直接通过counter变量创建方法,因为它会使类混乱,并且对具有许多变异方法的类型很烦人。同样,我知道我也可以将counter设置为常量,将Counter设置为类,但是我需要该属性的值类型语义。

1 个答案:

答案 0 :(得分:1)

您已正确列出所有可用选项。如果您要问的是,没有什么其他的技巧是您不知道的。这只是Swift的一个缺点,当您(经常发生)想要使用一个辅助结构时。

我一直在自己的代码中遇到此问题。例如:

final class ViewController: UIViewController {
    lazy var state = Mover(owner:self)

出于与您相同的原因,这并不是我真正想说的。移动器必须是可变的并且是结构。因此,从理论上讲,可以在我的state之上分配另一个Mover。我只需要与自己订立合同就不能这样做,可惜这并不容易执行。

如果您真正想做的就是防止以不同的间隔替换计数器,则可以使用didSet观察器(尽管强制执行是在运行时执行,而不是在编译时执行):

var counter = Counter() {
    didSet {
        if oldValue.interval != counter.interval {
            fatalError("hey")
        }
    }
}

但是您完全可以想象这会变得非常复杂。每个突变都会替代一个不同的Counter,因此要确保这种替代“只是”突变的结果,因此必须谨慎行事。同样,如果您打算采用这种方法,您可能希望将Counter本身的工作摆在知道什么构成“合法”突变上。在这种简单的情况下,我们也许可以使用相等性:

class Foo {
    var counter = Counter() {
        didSet {
            if oldValue != counter {
                fatalError("hey")
            }
        }
    }
    init() { }
}

struct Counter : Equatable {
    static func ==(lhs:Counter,rhs:Counter) -> Bool {
        return rhs.interval == lhs.interval
    }
    // the rest as before....
}