Ispirato implementazione findalls, ho costruire il mio generatore di Singleton, che è un po 'più potente.
È possibile creare un singleton di qualsiasi tipo di classe o struttura in Swift. L'unica cosa che devi fare è implementare uno dei due diversi protocolli per il tuo tipo e usare Swift 2.0 o più recente.
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
}
}
Questo può sembrare un po 'spaventoso, ma non abbiate paura di questo codice. La funzione pubblica all'interno dell'estensione del protocollo creerà per te due punti di accesso. Per esempio si sarà in grado di scrivere codice in questo modo la società:
// 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"
Se avete qualche idea di come migliorare questo codice e renderlo ancora più sicuro, per favore fatemelo sapere. Spingerò presto questo codice su GitHub (licenza MIT), in modo che tutti possano trarne vantaggio.
UPDATE: Ho modificato leggermente il codice in modo da poter passare un'istanza inizializzata personalizzata di una classe con la funzione setter quando è chiamata la prima volta.
UPDATE 2: Ho rimosso il protocollo ClassInstance e ho modificato la funzione di ripristino privato. Il protocollo Instance
viene ora chiamato SingletonType
. La funzione setter non è più opzionale. Al momento Xcode 7 beta 3 si blocca e fornisce un erroreillegal instruction: 4
quando si chiama il getter. Ma questo è un bug beta confermato.
Sembra promettente. Grazie :-) Vedrò se riesco a farlo funzionare oggi. –
C'è un motivo per usare 'flatMap'? 'Se let o = (istanze [nome] come? T)' sembra funzionare altrettanto bene. Ho dovuto cercare quel nuovo metodo poiché è nuovo con una delle ultime 1.2 beta. –
@ThomasKilian Scusa, hai ragione.Non sapevo che il 'if let' poteva scartare un valore di opzione doppiamente incartato,' Inizializzabile '. Grazie! – findall