如何构建一个可以控制其存储属性可变性的 Swift 对象

How to build a Swift object that can control the mutability of its stored properties

我想创建一组具有以下行为的对象:

  1. 每个都有一个 BOOL 属性 -- 称之为 dataLocked -- 最初是假的。
  2. 每个都有一组存储的属性,只要 dataLocked == false.
  3. ,其值就可以设置,但不能读取
  4. 每当 dataLocked == true
  5. 时,可以读取这些相同的存储属性,但不能设置它们
  6. dataLocked只能设置一次

下面是一个示例实现。是否有任何 Swifty 方法可以实现这一点,而不必为每个对象的每个 属性 重现所有这些获取和设置条件?

我认为最简洁的解决方案是创建一个 属性 包装器,但我还没有找到任何方法让包装器根据封闭对象中的“锁定”属性 的值更改其行为.

class ImmutableObjectBase {
    var dataLocked: Bool = false {
        didSet { dataLocked = true }
    }
    private var _someIntValue: Int = 42
    var someIntValue: Int {
        get {
            precondition(dataLocked, "Cannot access object properties until object is locked")
            return _someIntValue
        }
        set {
            precondition(!dataLocked, "Cannot modify object properties after object is locked")
            _someIntValue = newValue
        }
    }
}

let i = ImmutableObjectBase()
i.someIntValue = 100
i.dataLocked = true     // or false, it doesn't matter!
print (i.someIntValue)  // 100
print (i.dataLocked)    // true
i.someIntValue = 200    // aborts

我找到了解决您问题的方法,但这可能不是最干净的方法。 如果您检查 this,您可能会找到更好的方法 或 this.

我使用这个 tutorial:

设法锁定和解锁属性
import Foundation
import Combine

public class Locker: ObservableObject {
    @Published public var isLocked = false
    public static let shared = Locker()
    private init() {}
}

@propertyWrapper
struct Lockable<Value> {
    private var _wrappedValue: Value
    
    init(wrappedValue: Value) {
        self._wrappedValue = wrappedValue
    }
    
    var wrappedValue: Value {
        get {
            precondition(Locker.shared.isLocked, "Cannot access object properties until object is locked")
            return _wrappedValue
        }
        set {
            precondition(!Locker.shared.isLocked, "Cannot modify object properties after object is locked")
            _wrappedValue = newValue
        }
    }
}

class ImmutableObjectBase {
    var isLocked: Bool = false {
        didSet {
            Locker.shared.isLocked = self.isLocked
        }
    }
    @Lockable var someString: String = "initial"
    @Lockable var someInt: Int = 1
}


var i = ImmutableObjectBase()
i.isLocked = true
print(i.someInt, i.someString) // 1, initial
i.isLocked = false
i.someInt = 2
i.someString = "new value"
i.isLocked = true
print(i.someInt, i.someString) // 2, new value

编辑:

我刚刚发现了一个类似的问题,并且有一个可接受的答案。 检查

import Foundation

protocol PropertyWrapperWithLockableObject {
    var enclosingObject: LockableObjectBase! {get set}
}

@propertyWrapper
class Lockable<Value>: PropertyWrapperWithLockableObject {
    private var _wrappedValue: Value
    var enclosingObject: LockableObjectBase!
    
    init (wrappedValue: Value) { self._wrappedValue = wrappedValue }
    
    var wrappedValue: Value {
        get {
            precondition(enclosingObject.isLocked, "Cannot access object properties until object is locked")
            return _wrappedValue
        }
        set {
            precondition(!enclosingObject.isLocked, "Cannot modify object properties after object is locked")
            _wrappedValue = newValue
        }
    }
}

class LockableObjectBase {
    internal var isLocked: Bool = false {
        didSet { isLocked = true }
    }
    
    init () {
        let mirror = Mirror(reflecting: self)
        for child in mirror.children {
            if var child = child.value as? PropertyWrapperWithLockableObject {
                child.enclosingObject = self
            }
        }
    }
}

用法:

class DataObject: LockableObjectBase {
    @Lockable var someString: String = "Zork"
    @Lockable var someInt: Int
    
    override init() {
        someInt = 42
        // super.init() // Not needed in this particular example.
    }
}

var testObject = DataObject()
testObject.isLocked = true
print(testObject.someInt, testObject.someString) // 42, Zork
testObject.isLocked = false             // Has no effect: Object remained locked
print (testObject.isLocked)             // true
testObject.someInt = 2                  // Aborts the program

arsenius 的回答 提供了重要的反思线索!