Hi All,
Using protocols are quite important in case of “plugin” concept to implement
delegation: allow others to implement small components which will be run in
specific cases.
To support one component a delegate or closure is sufficient. For the delegate
we can use the protocol as referenced type unless it has associated type. If it
uses associated type we can still use Generics to reference the object and use
protocol only for constraints.
Allowing more components to subscribe causes the problem that we cannot
reference them:
· We cannot use generics as there can be any arbitrary number and type
of objects so we have to use protocols to reference them
· When there is an associatedtype in the protocol it is not possible to
use to reference objects
For example:
We have a generic class which holds a collection of some type of objects – the
generic parameter is a type of an item.
This class exposes a kind of delegate (specified by a protocol) and arbitrary
number of implementers can subscribe. These delegates will be called in a
specific case: for example when a new item is added to the collection.
The defined protocol uses associatedtype since the specific type of element is
unkown.
The problem that the code cannot compile because of the error: "protocol
'SomeProtocol' can only be used as a generic constraint because it has Self or
associated type requirements"
So we know the specific element we want to store but still cannot specify it.
We cannot use generic for the ‘delegates’ as it should contain different
implementations of SomeProtocol.
protocol SomeProtocol {
associatedtype ElementType
func someMethod(withElement: ElementType) -> Void
}
class SomeProtocolImplInt : SomeProtocol {
typealias ElementType = Int
var sum : Int = 0
func someMethod(withElement element: Int) {
sum += element
}
}
class Container<SomeElementType> {
typealias ElementType = SomeElementType
var elements = Array<SomeElementType>()
var delegates = Array<SomeProtocol where
SomeProtocol.ElementType==SomeElementType>() // ‘where’ could solve the problem
but not allowed here
func subscribe<D : SomeProtocol>(delegate : D) where
D.ElementType==SomeElementType { // would be better without generics as this
generates as many methods as many different types used to call it
delegates.append(delegate)
}
func add(element: SomeElementType) {
for delegate in delegates {
delegate.someMethod(withElement: element)
}
elements.append(element)
}
}
// usage:
var container = Container<Int>()
let calculator = SomeProtocolImplInt()
container.subscribe(delegate: calculator)
container.add(element: 1)
Workaround:
Use 'Thunks'. These are type eraser structs, which implements the same
protocol. For each method they referencing (capturing) the original methods and
properties of the object. So 'thunks' are technically proxy objects.
struct DirtyThunk<SomeElementType> : SomeProtocol {
private let _someMethod : (SomeElementType) -> Void
init<D : SomeProtocol>(delegate : D) where D.ElementType==SomeElementType {
_someMethod = delegate.someMethod
}
func someMethod(withElement: SomeElementType) {
_someMethod(withElement)
}
}
the subscribe method will be modified:
func subscribe<D : SomeProtocol>(delegate : D) where
D.ElementType==SomeElementType { // would be better without generics as this
generates as many methods as many different types used to call it
let thunk = DirtyThunk(delegate)
delegates.append(thunk)
}
This solution works - however we can never retrieve the original object any
more as it is not referenced. The implementation of the “thunks” are also
painful a bit. The methods are captured only by the name of the methods without
the parameters so leads the problem if different methods have the same prefix.
I tried to solve using ‘Any’ to reference the delegates but then cannot cast to
a proper type to call them.
Do one know any better solution?
Thanks,
Andras
_______________________________________________
swift-users mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-users