Swift 中的 Getters 和 Setters - 改用 WillSet 和 DidSet 有意义吗?
Getters and Setters in Swift - Does it make sense to use WillSet and DidSet instead?
我正在研究我们应该为属性使用 Get 和 Set 的原因。
我注意到了 3 个主要原因
- 当您想 do/check 在实际设置
属性
- 当你想拥有一个只能从中得到的属性
(我想也许出于安全目的?),或者给它不同的访问权限
级别。
- 隐藏 属性 的内部表示,同时暴露
属性 使用替代表示法。 (这对我来说没有
很有意义,因为我可以在错误的地方使用
无论如何设置功能)
下面的代码是一个示例,说明如何利用我提到的那 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
}
}
但是下面的另一个例子也利用了提到的那些点,但我没有使用另一个计算的 属性 到 return 私有 属性 值,我只是使用 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
}
}
所以我很想知道每个实现的优点和缺点是什么。
提前致谢
您的问题归结为编译时间与 运行 时间错误。解决您的 3 个问题:
- 是的,
willCheck
是您唯一的选择
- 只读属性分为两种类型:(a) 其值来自其他属性的属性,例如它们的总和; (b) 您希望自己能够更改的那些,而不是由用户更改的。第一种确实没有setter;第二种类型有一个 public 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
in Swift 2 与 Swift 3 的注释: 如果你在 Swift 中尝试这个2 playground,你会发现 t.greeting = "Goodbye world"
工作得很好。这是因为 Swift 2 有一个奇怪的访问级别说明符:private
表示 "only accessible within the current file"。将 class 定义和示例代码分开到不同的文件中,Xcode 会报错。在 Swift 3 中,它被更改为 fileprivate
,这既更清晰,又保存了 private
关键字,用于更类似于 Java 和 .NET
的内容
我正在研究我们应该为属性使用 Get 和 Set 的原因。
我注意到了 3 个主要原因
- 当您想 do/check 在实际设置 属性
- 当你想拥有一个只能从中得到的属性 (我想也许出于安全目的?),或者给它不同的访问权限 级别。
- 隐藏 属性 的内部表示,同时暴露 属性 使用替代表示法。 (这对我来说没有 很有意义,因为我可以在错误的地方使用 无论如何设置功能)
下面的代码是一个示例,说明如何利用我提到的那 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
}
}
但是下面的另一个例子也利用了提到的那些点,但我没有使用另一个计算的 属性 到 return 私有 属性 值,我只是使用 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
}
}
所以我很想知道每个实现的优点和缺点是什么。
提前致谢
您的问题归结为编译时间与 运行 时间错误。解决您的 3 个问题:
- 是的,
willCheck
是您唯一的选择 - 只读属性分为两种类型:(a) 其值来自其他属性的属性,例如它们的总和; (b) 您希望自己能够更改的那些,而不是由用户更改的。第一种确实没有setter;第二种类型有一个 public 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
in Swift 2 与 Swift 3 的注释: 如果你在 Swift 中尝试这个2 playground,你会发现 t.greeting = "Goodbye world"
工作得很好。这是因为 Swift 2 有一个奇怪的访问级别说明符:private
表示 "only accessible within the current file"。将 class 定义和示例代码分开到不同的文件中,Xcode 会报错。在 Swift 3 中,它被更改为 fileprivate
,这既更清晰,又保存了 private
关键字,用于更类似于 Java 和 .NET