ios Swift - 协议扩展 - 属性默认值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38885622/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Swift - Protocol extensions - Property default values
提问by Axort
Let's say that I have the following protocol:
假设我有以下协议:
protocol Identifiable {
var id: Int {get}
var name: String {get}
}
And that I have the following structs:
而且我有以下结构:
struct A: Identifiable {
var id: Int
var name: String
}
struct B: Identifiable {
var id: Int
var name: String
}
As you can see, I had to 'conform' to the Identifiable protocol in struct A and struct B. But imagine if I had N more structs that needs to conform to this protocol... I don't want to 'copy/paste' the conformance (var id: Int, var name: String)
如您所见,我必须“符合”结构 A 和结构 B 中的可识别协议。但是想象一下,如果我还有 N 个需要符合该协议的结构......我不想“复制/粘贴” ' 一致性(var id: Int, var name: String)
So I create a protocol extension:
所以我创建了一个协议扩展:
extension Identifiable {
var id: Int {
return 0
}
var name: String {
return "default"
}
}
With this extension now I can create a struct that conforms to the Identifiable protocol without having to implement both properties:
现在有了这个扩展,我可以创建一个符合 Identifiable 协议的结构,而不必实现这两个属性:
struct C: Identifiable {
}
Now the problem is that I can't set a value to the id property or the name property:
现在的问题是我无法为 id 属性或 name 属性设置值:
var c: C = C()
c.id = 12 // Cannot assign to property: 'id' is a get-only property
This happens because in the Identifiable protocol, id and name are only gettable. Now if I change the id and name properties to {get set}I get the following error:
发生这种情况是因为在 Identifiable 协议中,id 和 name 只能获取。现在,如果我将 id 和 name 属性更改为{get set},则会出现以下错误:
Type 'C' does not conform to protocol 'Identifiable'
类型“C”不符合协议“可识别”
This error happens because I haven't implemented a setter in the protocol extension... So I change the protocol extension:
发生此错误是因为我没有在协议扩展中实现 setter ......所以我更改了协议扩展:
extension Identifiable {
var id: Int {
get {
return 0
}
set {
}
}
var name: String {
get {
return "default"
}
set {
}
}
}
Now the error goes away but if I set a new value to id or name, it gets the default value (getter). Of course, the setter is empty.
现在错误消失了,但如果我为 id 或 name 设置一个新值,它会获得默认值(getter)。当然,setter 是空的。
My question is: What piece of code do I have to put inside the setter?Because if I add self.id = newValueit crashes (recursive).
我的问题是: 我必须在 setter 中放入什么代码?因为如果我添加self.id = newValue它会崩溃(递归)。
Thanks in advance.
提前致谢。
回答by Luca Angeletti
It seems you want to add a stored property
to a type via protocol extension. However this is not possible because with extensions you cannot add a stored property.
您似乎想stored property
通过协议扩展向类型添加 a 。但是这是不可能的,因为使用扩展您不能添加存储的属性。
I can show you a couple of alternatives.
我可以向您展示几个替代方案。
Subclassing (Object Oriented Programming)
子类化(面向对象编程)
The easiest way (as probably you already imagine) is using classes instead of structs.
最简单的方法(可能你已经想到了)是使用类而不是结构。
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.
缺点:在这种情况下,你的 A 类需要继承
IdentifiableBase
,因为在 Swift 中没有多重继承,这将是 A 唯一能够继承的类。
Components (Protocol Oriented Programming)
组件(面向协议的编程)
This technique is pretty popular in game development
这种技术在游戏开发中非常流行
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 }
}
}
Now you can make your type conform to Identifiable
simply writing
现在你可以让你的类型符合Identifiable
简单的写作
struct A: Identifiable {
var identifiableComponent = IdentifiableComponent()
}
Test
测试
var a = A()
a.identifiableComponent.name = "test"
print(a.identifiableComponent.name) // test
回答by Nick
Protocols and protocol extensions are very powerful, but they tend to be most useful for read-only properties and functions.
协议和协议扩展非常强大,但它们往往对只读属性和函数最有用。
for what you're trying to accomplish (stored properties with a default value), classes and inheritance might actually be the more elegant solution
对于您要完成的任务(具有默认值的存储属性),类和继承实际上可能是更优雅的解决方案
something like:
就像是:
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)")
回答by Harsh
This is why you were not able to set the properties.
这就是您无法设置属性的原因。
The property becomes a computed property which means it does not have a backing variable such as _x as it would in ObjC. In the solution code below you can see the xTimesTwo does not store anything, but simply computes the result from x.
该属性成为计算属性,这意味着它没有像在 ObjC 中那样的支持变量,例如 _x。在下面的解决方案代码中,您可以看到 xTimesTwo 不存储任何内容,而只是从 x 计算结果。
See Official docs on computed properties.
请参阅有关计算属性的官方文档。
The functionality you want might also be Property Observers.
您想要的功能也可能是属性观察器。
Setters/getters are different than they were in Objective-C.
Setter/getter 与它们在 Objective-C 中的不同。
What you need is:
你需要的是:
var x:Int
var xTimesTwo:Int {
set {
x = newValue / 2
}
get {
return x * 2
}
}
You can modify other properties within the setter/getters, which is what they are meant for
您可以修改 setter/getter 中的其他属性,这就是它们的用途
回答by DominicMDev
Objective-C Associated Objects
Objective-C 关联对象
You can use Objective-Cassociated objects to basically add a stored property
to a class
or protocol
.
Note that associated objects only work for class objects.
您可以使用Objective-C关联对象基本上将 a 添加stored property
到 aclass
或protocol
。
请注意,关联对象仅适用于类对象。
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)
}
}
}
Now you can make your class
conform to Identifiable
by simply writing
现在您可以通过简单地编写来使您class
符合Identifiable
class A: Identifiable {
}
Test
测试
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