为什么我可以做到这一点没有任何错误:
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定义),为什么我还要使用它们呢?
getter和setter要求可以通过各种方式的符合类型来满足。如果属性声明包含get和set关键字,则符合类型可以使用存储的变量属性或可读写的计算属性(即实现getter和setter的计算属性)来实现它。但是,该属性声明不能实现为常量属性或只读计算属性。如果属性声明仅包含get关键字,则可以将其实现为任何类型的属性。
苹果公司在"Swift Programming Language (Swift 3)"说:
如果协议只要求属性可以获取,那么任何类型的属性都可以满足要求,如果这对您自己的代码有用,则该属性也可以设置。
因此,以下五个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:计算属性(获取和设置)
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)
变量属性
/* 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"
Apple还声明:
如果协议要求属性可获取和可设置,则不能通过常量存储属性或只读计算属性来满足该属性要求。
因此,以下两个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'
示例#3:计算属性(仅限获取)
protocol FullyNamed {
var fullName: String { get }
}
struct Duck: FullyNamed {
var fullName: String {return "Scrooge McDuck"}
init(fullName: String) {
self.fullName = fullName
// Error Message Cannot assign to Property: "FullName" is get only
}
}
考虑以下:
var testDto = ModelDto(modelId: 1)
这里的变量testDto
类型已知为ModelDto
。已知ModelDto
有一个可变的变量var objectId: Int
。您可以自由地修改objectId,因为您通过ModelDto
接口访问该对象,而不是通过仅可获取的协议接口。
请尝试以下方法:
var testDto: DataTransferObject = ModelDto(modelId: 1)
testDto.objectId = 2 // compiler error
上面的例子不应该编译。因为testDto
的类型只知道是DataTransferObject
,所以我们不知道底层实现具有可设置的属性。我们只知道协议中声明的gettable属性。
简而言之,你已经声明ModelDto
有一个get / set变量,所以如果Swift没有让你设置它,那就太奇怪了。拥有一个get only变量将依赖于您通过协议引用对象或将objectId
上的ModelDTO
更改为let变量。
编辑:解决你为什么允许ModelDto
有一个可设置变量的困惑。它与ModelDto
如何被允许具有除协议中定义的功能之外的其他功能相同。 getter和setter实际上只是函数,因此需要getter的协议并不排除实现也有setter。目标C中也是如此。协议是描述性的,而非限制性的。
在您的类中,您将创建一个名为objectId
的存储属性。在您的协议中,您指定该属性需要一个getter - 这是它唯一的要求。
如果您希望它是计算机属性,就像您期望的那样,则需要使用以下内容声明objectId
:
var objectId: Int{ return (someNumber) }
如果没有闭包来计算值,默认情况下它是一个存储的属性。
我正在回答这个问题的一般意义。
在解决这个问题之前,你必须知道get
和set
是什么意思。
(如果你来自Objective-C世界:) get
意味着readOnly,那就是我被允许知道动物的腿数。我不被允许设置它。 get
和set
一起意味着readWrite,即我可以知道动物的体重,同时我也可以设置/改变动物的体重
使用以下示例。
protocol Animal {
var weight : Int { get set }
var limbs : Int { get }
}
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)
来实现预期的行为
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'
}
你不被允许隐藏,因为你答应提供一个二传手......
您在代码示例中看到的行为称为成员隐藏。成员隐藏发生在面向对象语言中,当新成员声明具有相同名称或继承的成员时,因此通过在结构实现中使用:var objectId: Int
,您实际上是在创建一个名为objectId的新成员并隐藏继承自该成员的属性。协议。
为了遵守结构和协议之间的契约,objectId可以声明为:
let objectId: Int = 1
要么
var objectId: Int {
get {
return 1
}
}