swift - example - Creando un singleton genérico
singleton swift 3 (2)
Esto es un poco loco (para mí). Básicamente quiero tener 2 singleton diferentes que hereden de la misma clase. En cualquiera de los dos quiero usar una cierta clase que se deriva. Entonces tengo Utility
y ambos AUtil:Utility
y BUtil:Utility
. Y Singleton
que se usa como ASingleton
usando AUtility
en su estómago y B
respectivamente. Fallé en todas las fronteras. El último intento fue un patrón de fábrica que simplemente convirtió Swift 1.2 en Segfault:
protocol Initializable { init() }
class A:Initializable {
var x = "A"
required init() {}
}
class B:Initializable {
var x = "B"
required init() {}
}
class C {
let t:Initializable
init(t:Initializable) {
self.t = t
println(t)
}
func factory() {
println(t.dynamicType())
}
}
Como dije, también intenté hacer el siguiente patrón genérico:
private let _SingletonSharedInstance = StaticClass()
class StaticClass {
class var sharedInstance : StaticClass {
return _SingletonSharedInstance
}
}
let s = StaticClass.sharedInstance
(Este no es genérico como ves, pero todos mis intentos fallaron y entonces muestro mi punto de partida).
De todos modos, parece que estoy perdido entre el destino y la muerte.
¿Te refieres a algo como esto?
protocol Initializable: class { init() }
private var instances = [String: Initializable]()
func singletonInstance<T: Initializable>(_ ty: T.Type = T.self) -> T {
let name = NSStringFromClass(ty)
if let o = (instances[name] as? T) {
return o
}
let o = ty()
instances[name] = o
return o
}
Un lado de uso del mismo, por ejemplo.
class Foo: Initializable { required init() {} }
class Bar: Initializable { required init() {} }
let foo1 = singletonInstance() as Foo // or `singletonInstance(Foo.self)`
let foo2 = singletonInstance() as Foo
assert(foo1 === foo2)
let bar1 = singletonInstance() as Bar
let bar2 = singletonInstance() as Bar
assert(bar1 === bar2)
(He probado el código anterior y lo he puesto a funcionar en Swift 1.2.)
Inspirado por la implementación de findalls, construyo mi propio generador singleton, que es un poco más potente.
Puede crear un singleton de cualquier clase o tipo de estructura en Swift. Lo único que debe hacer es implementar uno de dos protocolos diferentes para su tipo y usar Swift 2.0 o posterior.
public protocol SingletonType { init() }
private var singletonInstances = [String: SingletonType]()
extension SingletonType {
// this will crash Xcode atm. it''s a Swift 2.0 beta bug. Bug-ID: 21850697
public static var singleton: Self { return singleton { $0 } }
public static func singleton(setter: (_: Self) -> Self) -> Self {
guard let instance = singletonInstances["/(self)"] as? Self else {
return setInstance(self.init(), withSetter: setter, overridable: true)
}
return setInstance(instance, withSetter: setter, overridable: false)
}
private static func setInstance(var instance: Self, withSetter setter: (_: Self) -> Self, overridable: Bool) -> Self {
instance = restoreInstanceIfNeeded(instance1: instance, instance2: setter(instance), overridable: overridable)
singletonInstances["/(self)"] = instance
return instance
}
private static func restoreInstanceIfNeeded(instance1 i1: Self, instance2 i2: Self, overridable: Bool) -> Self {
// will work if the bug in Swift 2.0 beta is fixed !!! Bug-ID: 21850627
guard i1.dynamicType is AnyClass else { return i2 }
return ((i1 as! AnyObject) !== (i2 as! AnyObject)) && !overridable ? i1 : i2
}
}
Esto puede parecer un poco aterrador, pero no tenga miedo de este código. La función pública dentro de la extensión de protocolo creará dos puntos de acceso para usted. Por ejemplo, ahora podrá escribir código como este:
// extend your type: as an example I will extend ''Int'' here
extension Int : SingletonType {} // nothing else to do, because Int already has an ''init()'' initializer by default
// let the magic happen
Int.singleton // this will generate a singleton Int with 0 as default value
Int.singleton { (_) -> Int in 100 } // should set your Int singleton to 100
Int.singleton { $0 - 55 } // your singleton should be 45 now
// I need to mention that Xcode will produce the setter like this and trow an error
Int.singleton { (yourCustomInstanceName) -> Self in // replace ''Self'' with ''Int'' and you should be fine
return yourCustomInstanceName
}
// btw. we just ignored the return value everywhere
print(Int.singleton) // will print 45 here
var singleton2 = Int.singleton { $0 + 5 }
singleton2 += 10
print(Int.singleton) // should print 50, because ''singleton2'' is just a copy of an Int value type
class A : SingletonType {
var name = "no name"
required init() {}
}
A.singleton { $0; let i = A(); i.name = "hello world"; return i } // custom init on first singleton call for type A
print(A.singleton.name)
print(A.singleton { $0.name = "A"; return $0 }.name)
print(A.singleton.name)
// should print "hello world" and twice the string "A"
Si tiene alguna idea de cómo mejorar este código y hacerlo aún más seguro, hágamelo saber. Presionaré este código en GitHub (Licencia MIT) pronto, para que todos puedan beneficiarse de él.
ACTUALIZACIÓN: modifiqué un poco el código para que ahora pueda pasar una instancia inicializada personalizada de una clase con la función setter cuando se la llama por primera vez.
ACTUALIZACIÓN 2: eliminé el protocolo ClassInstance y modifiqué la función de restauración privada. El protocolo de Instance
ahora se llama SingletonType
. La función setter ya no es opcional. En este momento Xcode 7 beta 3 se bloqueará y proporcionará una illegal instruction: 4
error cuando llame al getter. Pero este es un error beta confirmado.