首先放个简单的Playground做下示范。
普通 protocol 中的 get set
protocol
中属性可以声明为{ get }
或者{ get set }
。大多数人第一眼肯定觉得{ get }
是约束为 read-only 属性,{ get set }
是约束为 read-write 属性。但是在protocol
中这些约束都是最小约束,也就是说{ get }
属性至少需要是 readable 的,至于它是否 writable 并不作要求;{ get set }
则明确的指出了属性必须是可读写,但是官方文档说明了,用{ get set }
声明的属性,不能在遵守该协议的类型中被实现为let
属性。
The getter and setter requirements can be satisfied by a conforming type in a variety of ways. If a property declaration includes both the
get
andset
keywords, a conforming type can implement it with a stored variable property or a computed property that is both readable and writeable (that is, one that implements both a getter and a setter). However, that property declaration can’t be implemented as a constant property or a read-only computed property. If a property declaration includes only theget
keyword, it can be implemented as any kind of property. For examples of conforming types that implement the property requirements of a protocol, see Property Requirements.
给属性设置默认值
给属性设置默认值?这难道不简单?
protocol FloatingViewProtocol {
var isDraggable: Bool { get }
}
extension FloatingViewProtocol {
var isDraggable: Bool {
return true
}
}
class FloatingView: FloatingViewProtocol {
var isDraggable = false
}
给一个{ get }
属性提供默认值,只需要在extension
中一个return
就搞定,确实很简单,但是这里出现了个问题,在尝试给FloatingView
对象的isDraggable
属性重新赋值时会报错,提示isDraggable is a get-only property
。
所以如果想要重新赋值,则该属性必须是{ get set }
的,可给{ get set }
属性提供默认值也比较尴尬:
protocol FloatingViewProtocol {
var isDraggable: Bool { get set }
}
extension FloatingViewProtocol {
var isDraggable: Bool {
get { return true }
set { }
}
}
class FloatingView: FloatingViewProtocol { }
如上确实提供了默认值,但是set { }
实在谈不上优雅,并且还有个致命的问题,isDraggable
不是个存储属性。如果FloatingView
在声明时采用了默认的isDraggable
值,那么给FloatingView
对象的isAutoAdsorb
属性重新赋值并不会被保存下来!
话说这个时候,我们是不是该联想一下属性和实例变量的关系 :)
class FloatingViewProtocolComponent {
var isDraggable = true
public init() {}
}
protocol FloatingViewProtocol {
var component: FloatingViewProtocolComponent { get }
var isDraggable: Bool { get set }
}
extension FloatingViewProtocol {
var isDraggable: Bool {
get { return component.isDraggable }
set { component.isDraggable = newValue }
}
}
class FloatingView: FloatingViewProtocol {
var component = FloatingViewProtocolComponent()
}
通过一个component
属性来实现类似实例变量的功能,操作还是有点骚。
实现 component 的 protocol 中的 get set
上面提到苹果文档指明了{ get set }
声明的属性不能被实现为let
。但是因为component
提供了默认值,那么该{ get set }
属性就可以不被实现,同时类可以声明一个同名同类型的let
属性覆盖协议中的属性,就造成了{ get set }
属性可以被声明为let
的假象。
总结一下可以被声明的属性类型
{ get }
属性:- read-only 计算属性
- read-wirte 计算属性
- var 存储属性(存储属性不能被重新赋值)
- let 存储属性
{ get set }
属性:- read-wirte 计算属性
- var 存储属性
- let 存储属性(需要用 component 类似的东西提供默认值)