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)
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:
struct C: Identifiable {
}
Now the problem is that I can't set a value to the id property or the name property:
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:
Type 'C' does not conform to protocol 'Identifiable'
This error happens because I haven't implemented a setter in the protocol extension... So I change the protocol extension:
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.
My question is: What piece of code do I have to put inside the setter? Because if I add self.id = newValue it crashes (recursive).
Thanks in advance.
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.
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.
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
struct A: Identifiable {
var identifiableComponent = IdentifiableComponent()
}
Test
var a = A()
a.identifiableComponent.name = "test"
print(a.identifiableComponent.name) // test
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)")
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.
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.
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
Objective-C Associated Objects
You can use Objective-C associated objects to basically add a stored property
to a class
or protocol
.
Note that associated objects only work for class objects.
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 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
来源:https://stackoverflow.com/questions/38885622/swift-protocol-extensions-property-default-values