1

我想创建一组表现出以下行为的对象:

  1. 每个都有一个 BOOL 属性——称之为dataLocked——最初是错误的。
  2. 每个都有一组存储的属性,其值可以在任何时候设置,但不能读取dataLocked == false
  3. 那些相同的存储属性可以在任何时候被读取,但不能被设置dataLocked == true
  4. dataLocked只能设置一次。

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

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

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
4

2 回答 2

0
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在这里的回答提供了重要的反思线索!

于 2022-02-15T16:36:14.330 回答
0

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

我设法通过使用本教程锁定和解锁属性:

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

编辑:

我刚刚发现了一个类似的问题,答案已被接受。在这里检查

于 2022-02-15T14:28:34.757 回答