Is it possible to make a generic unowned container for existential types? Something like:
public struct UnownedRef<T> {
unowned let instance: T // T should be proven at compile time to be an AnyObject
}
protocol Service: AnyObject {}
func foo(service: any Service) {
let unownedRef: UnownedRef<any Service > = UnownedRef(service)
}
Adding generic constraint T: AnyObject leads to compiler error Initializer 'init(_:)' requires that 'any Service' be a class type.
Though, I've managed to do a trick when conforming class can be accessed:
@propertyWrapper
public final class InitedOnDemandShared<P> { // P will be a protocol inherited form AnyObject
private weak var object: AnyObject?
private let builder: () -> AnyObject
public var wrappedValue: Object { ... }
/// proveSubTyping closure is not called in runtime, it is only needed at completime to prove that U conforms to P protocol
public init<U>(builder: @escaping () -> U, _ proveSubTyping: (U) -> P) where U: AnyObject {
self.builder = builder
}
}
// Usage
@InitedOnDemandShared var service: any Service
_service = InitedOnDemandShared(builder: { ServiceImpClass() }, { $0 })
Any ideas to generic UnownedRef for existentials can be done?
I know that unowned let unownedRef = service can be written. What I need is to make an array of unowned references for example. Another one problem is that unowned can not be used in protocol declarations:
protocol Assembly {
var service: any Service { get }
// unowned var service: any Service { get } // – compiler error
var service: UnownedRef<any Service> { get } // ok
}
You can use 'some' in the type for the parameter of foo and the UnownedRef.
Like this :
public struct UnownedRef<T: AnyObject> {
unowned let instance: T // T show be proven at compile time to be an AnyObject
}
protocol Service: AnyObject {
func printOk()
}
func foo(service: some Service) {
let unownedRef: UnownedRef<some Service > = UnownedRef(instance: service)
unownedRef.instance.printOk()
}
class MyClass: Service {
func printOk() {
print("ok")
}
}
let myClass = MyClass()
foo(service: myClass)
I can't use some Service because I get any Service form 3d party SDK.
So the task is to make unowned ref box for existentials. With opaque and generic types it is doable.
protocol Service: AnyObject {}
struct UnownedReference<T: AnyObject> {
unowned var reference: T
init(_ reference: T) { self.reference = reference }
}
protocol UnownedServiceReference {
func get() -> any Service
}
extension UnownedReference: UnownedServiceReference where T: Service {
func get() -> any Service { self.reference }
}
func makeUnownedReference(_ service: any Service) -> any UnownedServiceReference {
// We need a function to unwrap the 'any Service' existential
func makeRef(_ s: some Service) -> some UnownedServiceReference { UnownedReference(s) }
return makeRef(service)
}
You should be able to make an array of any UnownedServiceReference.
This is interesting idea, based on SE-0352, thanks.
I've also played with it and came up with this:
public struct UnownedRef<E> {
private unowned let _instance: AnyObject
public var wrappedValue: E { _instance as! E }
public init(_ instance: E, prove typeCastToAnyObject: (E) -> any AnyObject = { $0 }) {
let typeCasted = typeCastToAnyObject(instance)
assert((instance as AnyObject) === typeCasted)
self._instance = typeCasted
}
public init(_ instance: E) where E: AnyObject {
self._instance = instance
}
}
protocol Service: AnyObject {}
func foo(_ service: any Service) {
UnownedRef(service) // OK
}
func bar(_ instance: any CustomStringConvertible, integer: Int) {
UnownedRef(instance) // compiler error: requires that 'any CustomStringConvertible' be a class type
UnownedRef(integer) // compiler error: requires that 'Int' be a class type
}
func baz(_ object: NSObject) {
UnownedRef(object) // OK
UnownedRef(object as AnyObject) // OK
}