Swift协议只能设置?
为什么我可以做到这一点没有任何错误:
var testDto = ModelDto(modelId: 1) testDto.objectId = 2
而我定义这个:
protocol DataTransferObject { var objectType: DtoType { get } var parentObjectId: Int { get set } var objectId: Int { get } var objectName: String { get set } } struct ModelDto: DataTransferObject { var objectType: DtoType var parentObjectId: Int var objectId: Int var objectName: String init(modelId: Int) { self.objectType = DtoType.Model self.objectId = modelId self.parentObjectId = -1 self.objectName = String() } }
如果我的协议中的定义大部分被忽略(getter,setter定义),为什么我还要使用它们?
根据官方文件 :
可以通过各种方式满足吸气和吸气要求。 如果属性声明同时包含get和set关键字,则符合types可以使用存储的variables属性或可读写的计算属性(也就是实现getter和setter的属性)来实现它。 但是,该属性声明不能作为一个常量属性或一个只读的计算属性来实现。 如果一个属性声明只包含get关键字,它可以被实现为任何types的属性。
苹果在“Swift编程语言(Swift 3)”中表示 :
如果协议只需要一个属性是可以获取的,那么这个需求可以被任何types的属性满足,如果这个属性对你自己的代码有用的话,这个属性也是可以设置的。
出于这个原因,以下五个Playground代码片段都是有效的:
示例#1:不变的属性
protocol FullyNamed { var fullName: String { get } } struct Duck: FullyNamed { let fullName: String } let scrooge = Duck(fullName: "Scrooge McDuck") print(scrooge.fullName) // returns "Scrooge McDuck"
示例#2:可变属性
protocol FullyNamed { var fullName: String { get } } struct Duck: FullyNamed { var fullName: String } var scrooge = Duck(fullName: "Scrooge McDuck") print(scrooge.fullName) // returns "Scrooge McDuck" scrooge.fullName = "Scrooge H. McDuck" print(scrooge.fullName) // returns "Scrooge H. McDuck"
示例#3:计算属性(仅获取)
protocol FullyNamed { var fullName: String { get } } struct Duck: FullyNamed { private var name: String var fullName: String { return name } } let scrooge = Duck(name: "Scrooge McDuck") print(scrooge.fullName) // returns "Scrooge McDuck"
示例#4:计算属性(get和set)
protocol FullyNamed { var fullName: String { get } } struct Duck: FullyNamed { private var name: String var fullName: String { get { return name } set { name = newValue } } } var scrooge = Duck(name: "Scrooge McDuck") print(scrooge.fullName) // returns "Scrooge McDuck" scrooge.fullName = "Scrooge H. McDuck" print(scrooge.fullName) // returns "Scrooge H. McDuck"
示例#5: private(set)
variables属性
/* Duck.swift located in Sources folder */ protocol FullyNamed { var fullName: String { get } } public struct Duck: FullyNamed { public private(set) var fullName: String public init(fullName: String) { self.fullName = fullName } public mutating func renameWith(fullName: String) { self.fullName = fullName } } /* Playground file */ var scrooge = Duck(fullName: "Scrooge McDuck") print(scrooge.fullName) // returns "Scrooge McDuck" scrooge.renameWith("Scrooge H. McDuck") print(scrooge.fullName) // returns "Scrooge H. McDuck"
苹果还表示:
如果一个协议要求一个属性是可以获取和设置的,那么这个属性需求就不能通过一个常量存储属性或一个只读计算属性来实现。
出于这个原因,以下两个Playground代码片段无效:
示例#1:不变的属性
protocol FullyNamed { var fullName: String { get set } } struct Duck: FullyNamed { let fullName: String } let scrooge = Duck(fullName: "Scrooge McDuck") // Error message: Type 'Duck' does not conform to protocol 'FullyNamed'
示例#2:计算属性(仅获取)
protocol FullyNamed { var fullName: String { get set } } struct Duck: FullyNamed { private var name: String var fullName: String { return name } } var scrooge = Duck(name: "Scrooge McDuck") // Error message: Type 'Duck' does not conform to protocol 'FullyNamed'
在你的类中,你创build一个名为objectId
的存储属性。 在你的协议中,你指定该属性需要一个getter – 这是它唯一的要求。
如果你希望它是一个计算机属性,就像你期望的那样,你需要用下面的方法声明objectId
:
var objectId: Int{ return (someNumber) }
如果没有closures计算值,默认情况下,它是一个存储的属性。
考虑以下:
var testDto = ModelDto(modelId: 1)
这里的variablestestDto
types被称为ModelDto
。 已知ModelDto
具有可变variablesvar objectId: Int
。 您可以自由修改objectId,因为您通过ModelDto
接口访问对象,而不是通过只能获取的协议接口。
尝试以下操作:
var testDto: DataTransferObject = ModelDto(modelId: 1) testDto.objectId = 2 // compiler error
上面的例子不应该编译。 因为testDto
的types只知道DataTransferObject
,所以我们不知道底层的实现有一个可设置的属性。 我们只知道协议中声明的gettable属性。
简而言之,你已经声明了ModelDto
有一个get / setvariables,所以如果Swift不让你设置的话,这将是非常奇怪的。 拥有一个仅获取variables将依赖于通过协议引用对象或将ModelDTO
上的objectId
更改为letvariables。
编辑:为了解决你为什么ModelDto
被允许有一个可设置的variables混淆。 这与ModelDto
被允许具有除协议中定义的function之外的其他function相同。 Getter和Setter实际上只是函数,所以需要getter的协议并不排除也有setter的实现。 目标C中也是一样。协议是描述性的,而不是限制性的。
您在代码示例中看到的行为称为成员隐藏。 成员隐藏发生在面向对象的语言,当新的成员声明具有相同的名称或签名的var objectId: Int
,所以通过: var objectId: Int
在你的结构实现,你有效地创build一个新的成员称为objectId和隐藏属性从协议inheritance。
为了兑现你的结构和协议之间的契约, objectId可以被声明为:
let objectId: Int = 1
要么
var objectId: Int { get { return 1 } }
我正在回答一般意义上的问题。
在解决这个问题之前,你必须知道什么是set
。
(如果你来自一个Objective-C的世界:) get
means readOnly ,那就是允许我知道动物的腿数。 我不能设置它。 get
& set
together意思是readWrite,即我可以知道动物的重量,同时我也可以设置/改变动物的重量
用下面的例子。
protocol Animal { var weight : Int { get set } var limbs : Int { get } }
如果你只有getter,并试图隐藏setter(通过使用private (set)
…那么你将不会得到一个错误…这可能是你想要什么,必须如何做!
可能你想要的是:
class Cat : Animal { private (set) var limbs: Int = 4 // This is what you intended, because you only have get requirements...and don't want any conforming type to be able to set it ie don't want others do catInstance.limbs = 22 var weight: Int = 15 } var smallCat = Cat() smallCat.weight = 20 // Good! // attempting to set it will create an error!!! smallCat.limbs = 5 // Error: Cannot assign to property: 'limbs' setter is inaccessible
有可能你没有打算:
class Panda : Animal { var limbs: Int = 4 // This is OK, but it kinda defeats the purpose of it being a get only var weight: Int = 200 } var littlPanda = Panda() littlPanda.weight = 40 // Good littlPanda.limbs = 30 // NO Error!!! Likely unintended
基本上{get}
还有一些额外的工作要做,编译器不会告诉你…你必须添加private (set)
来实现预期的行为
如果你的财产有setter,而你试图隐藏setter,那么你实际上会看到一个错误。
class Dog : Animal { private (set) var limbs: Int = 4 private (set) var weight: Int = 50 // Error: Setter for property 'weight' must be declared internal because it matches a requirement in internal protocol 'Animal' }
你不能隐藏,因为你答应提供一个setter …