Swift中的Getter和Setter - 使用WillSet和DidSet是否有意义?

Hen*_*sta 8 oop get set swift computed-properties

我正在研究我们应该为我们的属性使用Get和Set的原因.

我注意到它的3个主要原因

  1. 如果您想在实际设置属性之前执行/检查某些内容
  2. 当你想拥有一个只能从中获取的属性时(可能出于安全目的,我猜?),或者给它不同的访问级别.
  3. 在使用替代表示公开属性的同时隐藏属性的内部表示.(这对我来说没有多大意义,因为我可以使用Set函数在错误的地方访问它)

下面的代码是一个如何在Swift中为属性实现Get和Set的示例,利用我提到的这3点:

class Test
{
    private var _testSet:String!
    private var _testGetOnly:String
    var testSet:String{
        get{
            return _testSet
        }
        set{
            _testSet = newValue + "you forgot this string"
        }
    }
    var testGetOnly:String!{
        get{
            return _testGetOnly
        }
    }

    init(testSet:String, testGetOnly:String)
    {
        _testSet = testSet
        _testGetOnly = testGetOnly
    }
}
Run Code Online (Sandbox Code Playgroud)

但是下面的另一个例子也利用了提到的那些点,但是不使用另一个计算属性来返回私有属性值,我只使用了willSet和didSet观察者

class Test
{
    var testGet:String {
        willSet{
            fatalError("Operation not allowed")
        }
    }
    var testWillSet:String!{
        didSet{
            self.testWillSet = self.testWillSet + "you forgot this string"
        }
    }
    init(testGet:String, testWillSet:String)
    {
        self.testGet = testGet
        self.testWillSet = testWillSet 
    }
}
Run Code Online (Sandbox Code Playgroud)

所以我很想知道每个实现的优点和缺点是什么.

提前致谢

Cod*_*ent 4

您的问题归结为编译时间与运行时错误。解决你的3个问题:

  1. 是的,willCheck这是你唯一的选择
  2. 只读属性分为两种类型:(a) 其值源自其他属性的属性,例如它们的总和;(b) 您希望自己能够更改而不是由用户更改的内容。第一种类型确实没有 setter;第二种类型有一个公共 getter 和一个私有 setter。编译器可以帮助您检查这一点,并且该程序将无法编译。如果你抛出一个fatalError运行didSet时错误,你的应用程序将会崩溃。
  3. 可能存在您不希望用户随意乱用的状态对象,是的,您可以完全向用户隐藏这些状态对象。

您的代码第一个示例在定义支持变量方面过于冗长 - 您不需要这样做。为了说明以下几点:

class Test
{
    // 1. Validate the new value
    var mustBeginWithA: String = "A word" {
        willSet {
            if !newValue.hasPrefix("A") {
                fatalError("This property must begin with the letter A")
            }
        }
    }

    // 2. A readonly property
    var x: Int = 1
    var y: Int = 2
    var total: Int {
        get { return x + y }
    }

    private(set) var greeting: String = "Hello world"
    func changeGreeting() {
        self.greeting = "Goodbye world" // Even for private property, you may still
                                        // want to set it, just not allowing the user
                                        // to do so
    }

    // 3. Hide implementation detail
    private var person = ["firstName": "", "lastName": ""]
    var firstName: String {
        get { return person["firstName"]! }
        set { person["firstName"] = newValue }
    }

    var lastName: String {
        get { return person["lastName"]! }
        set { person["lastName"] = newValue }
    }

    var fullName: String {
        get { return self.firstName + " " + self.lastName }
        set {
            let components = newValue.componentsSeparatedByString(" ")
            self.firstName = components[0]
            self.lastName = components[1]
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

let t = Test()
t.mustBeginWithA = "Bee"        // runtime error

t.total = 30                    // Won't compile

t.greeting = "Goodbye world"    // Won't compile. The compiler does the check for you
                                // instead of a crash at run time

t.changeGreeting()              // OK, greeting now changed to "Goodbye world"

t.firstName = "John"            // Users have no idea that they are actually changing 
t.lastName = "Smith"            // a key in the dictionary and there's no way for them
                                // to access that dictionary

t.fullName = "Bart Simpsons"    // You do not want the user to change the full name
                                // without making a corresponding change in the
                                // firstName and lastName. With a custome setter, you
                                // can update both firstName and lastName to maintain
                                // consistency
Run Code Online (Sandbox Code Playgroud)

关于 Swift 2 与 Swift 3 的注释private如果您在 Swift 2 Playground 中尝试此操作,您会发现t.greeting = "Goodbye world"效果很好。这是因为 Swift 2 有一个奇怪的访问级别说明符:private意味着“只能在当前文件内访问”。将类定义和示例代码分离到不同的文件中,Xcode 会报错。在 Swift 3 中,这一点已更改为fileprivate更清晰,并且保​​存了private与 Java 和 .NET 更相似的关键字