Ich habe eine Variable, die wie folgt initialisiert wurde:
lazy var aClient:Clinet = {
var _aClient = Clinet(ClinetSession.shared())
_aClient.delegate = self
return _aClient
}()
Das Problem ist, dass ich diese aClient
Variable irgendwann zurücksetzen muss , damit sie beim Ändern erneut initialisiert werden kann ClinetSession.shared()
. Wenn ich die Klasse jedoch auf optional setze Clinet?
, gibt LLVM eine Fehlermeldung aus, wenn ich versuche, sie auf zu setzen nil
. Wenn ich es nur irgendwo im Code mit zurücksetze aClient = Clinet(ClinetSession.shared())
, endet es mit EXEC_BAD_ACCESS
.
Gibt es eine Möglichkeit, sich lazy
selbst zurückzusetzen?
Clinet
undClinetSession
absichtlich oder ist es ein Tippfehler?Antworten:
faul ist explizit nur für die einmalige Initialisierung. Das Modell, das Sie übernehmen möchten, ist wahrscheinlich nur ein Initialisierungsmodell bei Bedarf:
var aClient:Client { if(_aClient == nil) { _aClient = Client(ClientSession.shared()) } return _aClient! } var _aClient:Client?
Wann immer dies der Fall
_aClient
istnil
, wird es initialisiert und zurückgegeben. Es kann durch Einstellen neu initialisiert werden_aClient = nil
quelle
self._aClient
in der verwendenget{}
. Manchmal kann der Swift-Compiler in einigen Fällen eine Variable finden, wenn dieself.
fehlt.Da sich das Verhalten von
lazy
in Swift 4 geändert hat , habe ich einigestruct
s geschrieben, die ein sehr spezifisches Verhalten ergeben, das sich zwischen den Sprachversionen niemals ändern sollte. Ich habe diese auf GitHub unter der BH-1-PD- Lizenz bereitgestellt : https://github.com/RougeWare/Swift-Lazy-PatternsResettableLazy
Hier ist die für diese Frage relevante, mit der Sie einen Wert träge initialisieren, diesen Wert zwischenspeichern und zerstören können, damit er später träge neu initialisiert werden kann.
Beachten Sie, dass dies Swift 5.1 erfordert! Für die Swift 4-Version siehe Version 1.1.1 dieses Repos .
Die einfache Verwendung ist sehr einfach:
@ResettableLazy var myLazyString = "Hello, lazy!" print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString) // Just returns the value "Hello, lazy!" _myLazyString.clear() print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString) // Just returns the value "Hello, lazy!" myLazyString = "Overwritten" print(myLazyString) // Just returns the value "Overwritten" _myLazyString.clear() print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
Dies wird gedruckt:
Wenn Sie über eine komplexe Initialisierungslogik verfügen, können Sie diese an den Eigenschaften-Wrapper übergeben:
func makeLazyString() -> String { print("Initializer side-effect") return "Hello, lazy!" } @ResettableLazy(initializer: makeLazyString) var myLazyString: String print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString) // Just returns the value "Hello, lazy!" _myLazyString.clear() print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString) // Just returns the value "Hello, lazy!" myLazyString = "Overwritten" print(myLazyString) // Just returns the value "Overwritten" _myLazyString.clear() print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
Sie können es auch direkt verwenden (als Property Wrapper installiert):
var myLazyString = ResettableLazy<String>() { print("Initializer side-effect") return "Hello, lazy!" } print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString.wrappedValue) // Just returns the value "Hello, lazy!" myLazyString.clear() print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString.wrappedValue) // Just returns the value "Hello, lazy!" myLazyString.wrappedValue = "Overwritten" print(myLazyString.wrappedValue) // Just returns the value "Overwritten" _myLazyString.clear() print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
Diese werden beide gedruckt:
Diese Antwort wurde aktualisiert. Die ursprüngliche Lösung funktioniert nicht mehr in Swift 4 und höher.
Stattdessen empfehle ich Ihnen, eine der oben aufgeführten Lösungen oder die Lösung von @ PBosman zu verwenden
Zuvor hing diese Antwort vom Verhalten ab, das ein Fehler war. Sowohl diese alte Version dieser Antwort als auch ihr Verhalten und warum es sich um einen Fehler handelt, werden im Text und in den Kommentaren des Swift-Fehlers SR-5172 (der ab dem 14.07.2017 mit PR # 10.911 behoben wurde ) beschrieben, und es ist klar dass dieses Verhalten nie beabsichtigt war.
Diese Lösung befindet sich im Text dieses Swift-Fehlers und auch im Verlauf dieser Antwort . Da es sich jedoch um einen Fehler-Exploit handelt, der in Swift 3.2+ nicht funktioniert, empfehle ich, dies nicht zu tun.
quelle
!
, wenn man dieses Muster verwenden würde, müsste jeder Aufrufer dieser Variablen das noch auspackenOptional
. ZBprint(aClient) // prints "Optional(Clinet)\n"
EDIT: Gemäß der Antwort von Ben Leggiero können faule Vars
nil
in Swift 3 fähig sein. EDIT 2: Scheint, als obnil
faule Vars nicht mehr in der Lage sind.Sehr spät zur Party und nicht einmal sicher, ob dies in Swift 3 relevant sein wird, aber hier geht es weiter. Davids Antwort ist gut, aber wenn Sie viele faule Null-Vars erstellen möchten, müssen Sie einen ziemlich umfangreichen Codeblock schreiben. Ich versuche, ein ADT zu erstellen, das dieses Verhalten kapselt. Folgendes habe ich bisher:
struct ClearableLazy<T> { private var t: T! private var constructor: () -> T init(_ constructor: @escaping () -> T) { self.constructor = constructor } mutating func get() -> T { if t == nil { t = constructor() } return t } mutating func clear() { t = nil } }
Sie würden dann Eigenschaften wie diese deklarieren und verwenden:
var aClient = ClearableLazy(Client.init) aClient.get().delegate = self aClient.clear()
Es gibt Dinge, die mir noch nicht gefallen, die ich aber nicht verbessern kann:
get()
auf einer Eigenschaft jedes Mal , die Sie verwenden möchten es ist schrecklich. Es wäre etwas besser, wenn dies eine berechnete Eigenschaft wäre, keine Funktion, aber berechnete Eigenschaften können nicht mutieren.get()
, müssen Sie jeden Typ, für den Sie dies verwenden möchten, um Initialisierer für erweiternClearableLazy
.Wenn jemand Lust hat, es von hier abzuholen, wäre das großartig.
quelle
Auf diese Weise kann die Eigenschaft so eingestellt werden, dass
nil
eine Neuinitialisierung erzwungen wird:private var _recordedFileURL: NSURL! /// Location of the recorded file private var recordedFileURL: NSURL! { if _recordedFileURL == nil { let file = "recording\(arc4random()).caf" let url = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(file) NSLog("FDSoundActivatedRecorder opened recording file: %@", url) _recordedFileURL = url } return _recordedFileURL }
quelle
Swift 5.1 :
class Game { private var _scores: [Double]? = nil var scores: [Double] { if _scores == nil { print("Computing scores...") _scores = [Double](repeating: 0, count: 3) } return _scores! } func resetScores() { _scores = nil } }
So verwenden Sie:
var game = Game() print(game.scores) print(game.scores) game.resetScores() print(game.scores) print(game.scores)
Dies erzeugt die folgende Ausgabe:
Computing scores... [0.0, 0.0, 0.0] [0.0, 0.0, 0.0] Computing scores... [0.0, 0.0, 0.0] [0.0, 0.0, 0.0]
Swift 5.1 und Property Wrapper
@propertyWrapper class Cached<Value: Codable> : Codable { var cachedValue: Value? var setter: (() -> Value)? // Remove if you don't need your Value to be Codable enum CodingKeys: String, CodingKey { case cachedValue } init(setter: @escaping () -> Value) { self.setter = setter } var wrappedValue: Value { get { if cachedValue == nil { cachedValue = setter!() } return cachedValue! } set { cachedValue = nil } } } class Game { @Cached(setter: { print("Computing scores...") return [Double](repeating: 0, count: 3) }) var scores: [Double] }
Wir setzen den Cache zurück, indem wir ihn auf einen beliebigen Wert setzen:
var game = Game() print(game.scores) print(game.scores) game.scores = [] print(game.scores) print(game.scores)
quelle
Hier gibt es einige gute Antworten.
Das Zurücksetzen eines Lazy Var ist in der Tat in vielen Fällen wünschenswert.
Ich denke, Sie können auch einen Abschluss definieren, um einen Client zu erstellen und Lazy Var mit diesem Abschluss zurückzusetzen. Etwas wie das:
class ClientSession { class func shared() -> ClientSession { return ClientSession() } } class Client { let session:ClientSession init(_ session:ClientSession) { self.session = session } } class Test { private let createClient = {()->(Client) in var _aClient = Client(ClientSession.shared()) print("creating client") return _aClient } lazy var aClient:Client = createClient() func resetClient() { self.aClient = createClient() } } let test = Test() test.aClient // creating client test.aClient // reset client test.resetClient() // creating client test.aClient
quelle
Wenn das Ziel darin besteht, eine faule Eigenschaft neu zu initialisieren, aber nicht unbedingt auf Null zu setzen (Gebäude aus Phlippie Bosman und Ben Leggiero), vermeiden Sie hiermit bedingte Überprüfungen jedes Mal, wenn der Wert gelesen wird:
public struct RLazy<T> { public var value: T private var block: () -> T public init(_ block: @escaping () -> T) { self.block = block self.value = block() } public mutating func reset() { value = block() } }
Zu testen:
var prefix = "a" var test = RLazy { () -> String in return "\(prefix)b" } test.value // "ab" test.value = "c" // Changing value test.value // "c" prefix = "d" test.reset() // Resetting value by executing block again test.value // "db"
quelle