为什么我可以毫无错误地执行此操作:
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 definition),为什么我还要使用它们呢?
答案 0 :(得分:43)
Apple在"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
}
}
答案 1 :(得分:22)
getter和setter要求可以通过各种方式的符合类型来满足。如果属性声明包含get和set关键字,则符合类型可以使用存储的变量属性或可读写的计算属性(即实现getter和setter的计算属性)来实现它。但是,该属性声明不能实现为常量属性或只读计算属性。 如果属性声明仅包含get关键字,则可以将其实现为任何类型的属性。
答案 2 :(得分:2)
请考虑以下事项:
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也是如此。协议是描述性的,而非限制性的。
答案 3 :(得分:1)
在您的课程中,您将创建一个名为objectId
的存储属性。在您的协议中,您指定该属性需要一个getter - 这是它唯一的要求。
如果您希望它是计算机属性,就像您期望的那样,则需要使用以下内容声明objectId
:
var objectId: Int{ return (someNumber) }
如果没有闭包来计算值,默认情况下它是一个存储属性。
答案 4 :(得分:1)
我回答了这个问题的一般意义。
在解决问题之前,你必须知道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}
还有一些额外的工作要完成,编译器不会告诉你...你必须添加{ {1}}实现预期的行为
private (set)
你不允许隐藏,因为你答应提供一个二传手......
答案 5 :(得分:0)
您在代码示例中看到的行为称为成员隐藏。当使用相同的名称或继承的成员声明新成员时,成员隐藏发生在面向对象的语言中,因此具有:
var objectId: Int
在结构实现中,您实际上是在创建一个名为objectId的新成员,并隐藏从协议继承的属性。
为了遵守您的结构和协议之间的合同, objectId 可以声明为:
let objectId: Int = 1
或
var objectId: Int {
get {
return 1
}
}