Swift - 协议扩展 - 属性 默认值
Swift - Protocol extensions - Property default values
假设我有以下协议:
protocol Identifiable {
var id: Int {get}
var name: String {get}
}
而且我有以下结构:
struct A: Identifiable {
var id: Int
var name: String
}
struct B: Identifiable {
var id: Int
var name: String
}
如您所见,我必须 'conform' 结构 A 和结构 B 中的 Identifiable 协议。但想象一下,如果我有 N 个更多的结构需要符合此协议...我不不想 'copy/paste' 一致性(var id:Int,var name:String)
所以我创建了一个协议扩展:
extension Identifiable {
var id: Int {
return 0
}
var name: String {
return "default"
}
}
现在有了这个扩展,我可以创建一个符合 Identifiable 协议的结构,而无需实现这两个属性:
struct C: Identifiable {
}
现在的问题是我无法为 ID 属性 或名称 属性:
设置值
var c: C = C()
c.id = 12 // Cannot assign to property: 'id' is a get-only property
发生这种情况是因为在Identifiable 协议中,id 和name 只能获取。现在,如果我将 id 和 name 属性更改为 {get set} 我会收到以下错误:
类型'C'不符合协议'Identifiable'
发生这个错误是因为我没有在协议扩展中实现setter...所以我更改了协议扩展:
extension Identifiable {
var id: Int {
get {
return 0
}
set {
}
}
var name: String {
get {
return "default"
}
set {
}
}
}
现在错误消失了,但如果我为 id 或 name 设置一个新值,它将获得默认值 (getter)。当然,setter是空的。
我的问题是:
我必须在 setter 中放入什么代码? 因为如果我添加 self.id = newValue 它会崩溃(递归)。
提前致谢。
协议和协议扩展非常强大,但它们往往对只读属性和函数最有用。
对于您要实现的目标(具有默认值的存储属性),类 和继承实际上可能是更优雅的解决方案
类似于:
class Identifiable {
var id: Int = 0
var name: String = "default"
}
class A:Identifiable {
}
class B:Identifiable {
}
let a = A()
print("\(a.id) \(a.name)")
a.id = 42
a.name = "foo"
print("\(a.id) \(a.name)")
您似乎想通过协议扩展向类型添加 stored property
。但是,这是不可能的,因为使用扩展程序您无法添加存储的 属性.
我可以告诉你几个选择。
子类化(面向对象编程)
最简单的方法(可能您已经想到了)是使用 类 而不是结构。
class IdentifiableBase {
var id = 0
var name = "default"
}
class A: IdentifiableBase { }
let a = A()
a.name = "test"
print(a.name) // test
Cons: In this case your A class needs to inherit from IdentifiableBase
and since in Swift theres is not multiple inheritance this will be the only class A will be able to inherit from.
组件(面向协议的编程)
这种技术在游戏开发中很流行
struct IdentifiableComponent {
var id = 0
var name = "default"
}
protocol HasIdentifiableComponent {
var identifiableComponent: IdentifiableComponent { get set }
}
protocol Identifiable: HasIdentifiableComponent { }
extension Identifiable {
var id: Int {
get { return identifiableComponent.id }
set { identifiableComponent.id = newValue }
}
var name: String {
get { return identifiableComponent.name }
set { identifiableComponent.name = newValue }
}
}
现在你可以让你的类型符合 Identifiable
只需写
struct A: Identifiable {
var identifiableComponent = IdentifiableComponent()
}
测试
var a = A()
a.identifiableComponent.name = "test"
print(a.identifiableComponent.name) // test
这就是您无法设置属性的原因。
属性 变成了计算的 属性,这意味着它没有像在 ObjC 中那样的支持变量,例如 _x。在下面的解决方案代码中,您可以看到 xTimesTwo 不存储任何内容,而只是计算 x 的结果。
请参阅有关计算属性的官方文档。
您想要的功能也可能是 属性 观察者。
Setters/getters 与 Objective-C 中的不同。
您需要的是:
var x:Int
var xTimesTwo:Int {
set {
x = newValue / 2
}
get {
return x * 2
}
}
您可以修改 setter/getters 中的其他属性,这正是它们的用途
Objective-C 关联对象
您可以使用 Objective-C 关联对象基本上将 stored property
添加到 class
或 protocol
。
请注意,关联对象仅适用于 class 个对象。
import ObjectiveC.runtime
protocol Identifiable: class {
var id: Int { get set }
var name: String { get set }
}
var IdentifiableIdKey = "kIdentifiableIdKey"
var IdentifiableNameKey = "kIdentifiableNameKey"
extension Identifiable {
var id: Int {
get {
return (objc_getAssociatedObject(self, &IdentifiableIdKey) as? Int) ?? 0
}
set {
objc_setAssociatedObject(self, &IdentifiableIdKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
var name: String {
get {
return (objc_getAssociatedObject(self, &IdentifiableNameKey) as? String) ?? "default"
}
set {
objc_setAssociatedObject(self, &IdentifiableNameKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
现在你可以让你的 class
符合 Identifiable
只需写
class A: Identifiable {
}
测试
var a = A()
print(a.id) // 0
print(a.name) // default
a.id = 5
a.name = "changed"
print(a.id) // 5
print(a.name) // changed
假设我有以下协议:
protocol Identifiable {
var id: Int {get}
var name: String {get}
}
而且我有以下结构:
struct A: Identifiable {
var id: Int
var name: String
}
struct B: Identifiable {
var id: Int
var name: String
}
如您所见,我必须 'conform' 结构 A 和结构 B 中的 Identifiable 协议。但想象一下,如果我有 N 个更多的结构需要符合此协议...我不不想 'copy/paste' 一致性(var id:Int,var name:String)
所以我创建了一个协议扩展:
extension Identifiable {
var id: Int {
return 0
}
var name: String {
return "default"
}
}
现在有了这个扩展,我可以创建一个符合 Identifiable 协议的结构,而无需实现这两个属性:
struct C: Identifiable {
}
现在的问题是我无法为 ID 属性 或名称 属性:
设置值var c: C = C()
c.id = 12 // Cannot assign to property: 'id' is a get-only property
发生这种情况是因为在Identifiable 协议中,id 和name 只能获取。现在,如果我将 id 和 name 属性更改为 {get set} 我会收到以下错误:
类型'C'不符合协议'Identifiable'
发生这个错误是因为我没有在协议扩展中实现setter...所以我更改了协议扩展:
extension Identifiable {
var id: Int {
get {
return 0
}
set {
}
}
var name: String {
get {
return "default"
}
set {
}
}
}
现在错误消失了,但如果我为 id 或 name 设置一个新值,它将获得默认值 (getter)。当然,setter是空的。
我的问题是: 我必须在 setter 中放入什么代码? 因为如果我添加 self.id = newValue 它会崩溃(递归)。
提前致谢。
协议和协议扩展非常强大,但它们往往对只读属性和函数最有用。
对于您要实现的目标(具有默认值的存储属性),类 和继承实际上可能是更优雅的解决方案
类似于:
class Identifiable {
var id: Int = 0
var name: String = "default"
}
class A:Identifiable {
}
class B:Identifiable {
}
let a = A()
print("\(a.id) \(a.name)")
a.id = 42
a.name = "foo"
print("\(a.id) \(a.name)")
您似乎想通过协议扩展向类型添加 stored property
。但是,这是不可能的,因为使用扩展程序您无法添加存储的 属性.
我可以告诉你几个选择。
子类化(面向对象编程)
最简单的方法(可能您已经想到了)是使用 类 而不是结构。
class IdentifiableBase {
var id = 0
var name = "default"
}
class A: IdentifiableBase { }
let a = A()
a.name = "test"
print(a.name) // test
Cons: In this case your A class needs to inherit from
IdentifiableBase
and since in Swift theres is not multiple inheritance this will be the only class A will be able to inherit from.
组件(面向协议的编程)
这种技术在游戏开发中很流行
struct IdentifiableComponent {
var id = 0
var name = "default"
}
protocol HasIdentifiableComponent {
var identifiableComponent: IdentifiableComponent { get set }
}
protocol Identifiable: HasIdentifiableComponent { }
extension Identifiable {
var id: Int {
get { return identifiableComponent.id }
set { identifiableComponent.id = newValue }
}
var name: String {
get { return identifiableComponent.name }
set { identifiableComponent.name = newValue }
}
}
现在你可以让你的类型符合 Identifiable
只需写
struct A: Identifiable {
var identifiableComponent = IdentifiableComponent()
}
测试
var a = A()
a.identifiableComponent.name = "test"
print(a.identifiableComponent.name) // test
这就是您无法设置属性的原因。
属性 变成了计算的 属性,这意味着它没有像在 ObjC 中那样的支持变量,例如 _x。在下面的解决方案代码中,您可以看到 xTimesTwo 不存储任何内容,而只是计算 x 的结果。
请参阅有关计算属性的官方文档。
您想要的功能也可能是 属性 观察者。
Setters/getters 与 Objective-C 中的不同。
您需要的是:
var x:Int
var xTimesTwo:Int {
set {
x = newValue / 2
}
get {
return x * 2
}
}
您可以修改 setter/getters 中的其他属性,这正是它们的用途
Objective-C 关联对象
您可以使用 Objective-C 关联对象基本上将 stored property
添加到 class
或 protocol
。
请注意,关联对象仅适用于 class 个对象。
import ObjectiveC.runtime
protocol Identifiable: class {
var id: Int { get set }
var name: String { get set }
}
var IdentifiableIdKey = "kIdentifiableIdKey"
var IdentifiableNameKey = "kIdentifiableNameKey"
extension Identifiable {
var id: Int {
get {
return (objc_getAssociatedObject(self, &IdentifiableIdKey) as? Int) ?? 0
}
set {
objc_setAssociatedObject(self, &IdentifiableIdKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
var name: String {
get {
return (objc_getAssociatedObject(self, &IdentifiableNameKey) as? String) ?? "default"
}
set {
objc_setAssociatedObject(self, &IdentifiableNameKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
现在你可以让你的 class
符合 Identifiable
只需写
class A: Identifiable {
}
测试
var a = A()
print(a.id) // 0
print(a.name) // default
a.id = 5
a.name = "changed"
print(a.id) // 5
print(a.name) // changed