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

时间:2016-08-31 16:32:10

标签: swift oop get set computed-properties

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

我注意到它有三个主要原因

  1. 如果您想在实际设置之前进行/检查 属性
  2. 如果您想拥有一个只能从中获取的属性 (可能出于安全考虑,我猜?),或给它不同的访问权限 水平。
  3. 在暴露a时隐藏属性的内部表示 使用替代表示的财产。 (对我来说没有 因为我可以使用在错误的地方访问它,所以很有意义 无论如何,设置功能)
  4. 下面的代码是一个如何在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
        }
    }
    

    但是下面的另一个例子也利用了上面提到的那些点,而不是使用另一个计算属性来返回私有属性值我只使用了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 
        }
    }
    

    所以我很想知道每种实施的优点和缺点是什么。

    提前致谢

1 个答案:

答案 0 :(得分:4)

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

  1. 是的,willCheck是您唯一的选择
  2. 只读属性分为两种类型:(a)其值来自其他属性的那些属性,例如,它们的总和; (b)您希望能够自行更改但不能由用户更改的内容。第一种类型真的没有二传;第二种类型有一个公共getter和一个私有setter 。编译器可以帮助您检查它,程序将无法编译。如果在fatalError中抛出didSet,则会出现运行时错误,并且您的应用程序将崩溃。
  3. 可能存在一些您不希望用户自由处理的状态对象,是的,您可以完全隐藏用户的状态。
  4. 您的代码第一个示例在定义支持变量时过于冗长 - 您不需要这样做。为了说明这些要点:

    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]
            }
        }
    }
    

    用法:

    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
    

    关于Swift 2与Swift 3中的private的说明:如果您在Swift 2游乐场中尝试此操作,您会发现t.greeting = "Goodbye world"工作正常。这是因为Swift 2有一个奇怪的访问级别说明符:private表示“只能在当前文件中访问”。将类定义和示例代码分成不同的文件,Xcode会抱怨。在Swift 3中,它被更改为fileprivate,它更清晰,并保存private关键字,以便更类似于Java和.NET