Swift 中的 Getter 和 Setter - 改用 WillSet 和 DidSet 有意义吗?
Posted
技术标签:
【中文标题】Swift 中的 Getter 和 Setter - 改用 WillSet 和 DidSet 有意义吗?【英文标题】:Getters and Setters in Swift - Does it make sense to use WillSet and DidSet instead? 【发布时间】:2017-01-08 08:19:57 【问题描述】:我正在研究我们应该对属性使用 Get 和 Set 的原因。
我注意到了 3 个主要原因
-
当您想在实际设置之前做/检查某事时
财产
当你想拥有一个只能从中获取的属性时
(我猜可能是出于安全目的?),或者给它不同的访问权限
级别。
在暴露属性的同时隐藏属性的内部表示
使用替代表示的属性。 (对我来说不是
很有意义,因为我可以使用在错误的地方访问它
无论如何设置函数)
下面的代码是一个示例,说明如何利用我提到的这 3 点在 Swift 中为属性实现 Get 和 Set:
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
所以我很想知道每种实现的优点和缺点是什么。
提前致谢
【问题讨论】:
非常有趣的问题,但我帮不了你,因为已经发现了暴徒...... willSet 和 didSet 是观察者。它们与 getter/setter 完全不同。 (甚至不要提到 Swift 中的属性(例如协议扩展中)通常甚至不是属性,它们是计算属性。)在回答您的问题时,是的,无论如何,这两个观察者都是如此出色的语言功能,通常情况下,您不必费心不再使用乏味的属性/getter/setter 模式 - 太好了。 【参考方案1】:您的问题归结为编译时间与运行时间错误。解决您的 3 个问题:
-
是的,
willCheck
是您唯一的选择
只读属性分为两种类型: (a) 其值来自其他属性的那些,例如,它们的总和; (b) 那些您希望能够自己更改但不能由用户更改的内容。第一种确实没有二传手;第二种类型有一个公共 getter 和一个 private setter。编译器可以帮助您检查,并且程序不会编译。如果您在 didSet
中抛出 fatalError
,则会出现运行时错误,并且您的应用程序将崩溃。
可能存在您不希望用户随意混淆的状态对象,是的,您可以完全对用户隐藏这些状态对象。
您的代码第一个示例在定义支持变量时过于冗长 - 您不需要这样做。为了说明这些要点:
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
关于private
在 Swift 2 与 Swift 3 中的说明:如果你在 Swift 2 的操场上尝试这个,你会发现 t.greeting = "Goodbye world"
工作得很好。这是因为 Swift 2 有一个奇怪的访问级别说明符:private
表示“只能在当前文件中访问”。将类定义和示例代码分开到不同的文件中,Xcode 会报错。在 Swift 3 中,这已更改为 fileprivate
,这既更清晰,又将 private
关键字保存为更类似于 Java 和 .NET 的内容
【讨论】:
以上是关于Swift 中的 Getter 和 Setter - 改用 WillSet 和 DidSet 有意义吗?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Swift 不像 Java 或 C# 那样对属性使用 getter 和 setter?