У меня есть переменная, которая инициализировала как:
lazy var aClient:Clinet = {
var _aClient = Clinet(ClinetSession.shared())
_aClient.delegate = self
return _aClient
}()
Проблема, в какой-то момент, я должен сбросить это aClient
переменная, таким образом, это может инициализировать снова когда ClinetSession.shared()
измененный. Но если я установил класс на дополнительный Clinet?
, LLVM даст мне ошибку, когда я попытаюсь установить его на nil
. Если я просто сбросил его где-нибудь в использовании кода aClient = Clinet(ClinetSession.shared())
, это закончится с EXEC_BAD_ACCESS
.
Есть ли путь, который может использовать lazy
и быть позволенным сбросить себя?
ленивый явно для единовременной инициализации. Модель, которую Вы хотите принять, является, вероятно, просто initialize-on-demand моделью:
var aClient:Client {
if(_aClient == nil) {
_aClient = Client(ClientSession.shared())
}
return _aClient!
}
var _aClient:Client?
Теперь каждый раз, когда _aClient
nil
, это будет инициализировано и возвращено. Это может быть повторно инициализировано установкой _aClient = nil
Поскольку поведение lazy
измененный в Swift 4, я записал некоторым struct
с, которые дают очень определенное поведение, которое никогда не должно изменяться между языковыми версиями. Я поместил их на GitHub, под [1 114] лицензия BH-0-PD: https://github.com/RougeWare/Swift-Lazy-Patterns
ResettableLazy
Здесь является одним соответствующим для этого вопроса, который дает Вам, путь к лениво - инициализирует значение, кэш, которые оценивают, и уничтожают его так, это может быть лениво повторно инициализировано позже.
Примечание, что это требует Swift 5.1! Для Swift 4 версии, см. версия 1.1.1 того repo.
простое использование этого очень просто:
@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!"
Это распечатает:
Hello, lazy!
Hello, lazy!
Hello, lazy!
Hello, lazy!
Overwritten
Hello, lazy!
, Если у Вас есть сложная логика инициализатора, можно передать это обертке свойства:
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!"
можно также использовать его непосредственно (вместо этого как обертка свойства):
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!"
Они оба распечатают:
Initializer side-effect
Hello, lazy!
Hello, lazy!
Initializer side-effect
Hello, lazy!
Hello, lazy!
Overwritten
Initializer side-effect
Hello, lazy!
<час> Вместо этого я рекомендую использовать одно из решений, упомянутых выше, или , решением
@PBosman ниже поведения была ошибка, описанная в [1 119] ошибка SR-5172 Swift (который был разрешен с 14.07.2017 с [1 120] PR № 10,911 ), и ясно, что это поведение никогда не было намеренным.
решение для Swift 3 ниже по историческим причинам, но потому что это - использование ошибки, которое не работает в Swift 3.2 + , я рекомендую сделать не , делают это:
<час>я не уверен точно, когда это было добавлено, но с [1 141] Swift 3, можно просто сделать свойство способным нолем:
lazy var aClient:Client! = { var _aClient = Client(ClinetSession.shared()) _aClient.delegate = self return _aClient }()
Теперь, в следующий раз, когда Вы называете aClient после установки его к
// ...
aClient = nilnil
, это будет повторно инициализировано. Примечание---, что, хотя это является теперь технически дополнительным, каждый раз, Вы пытаетесь считать его, это, как гарантируют, будет иметь значение во время выполнения. Вот почему я использую!
, здесь, потому что это всегда - безопасный вызов и никогда не будет , читает как [1 111], но это может быть , устанавливает на [1 112].
РЕДАКТИРОВАНИЕ: Согласно ответу Ben Leggiero, ленивый Вар может быть nil
способен в Swift 3. РЕДАКТИРОВАНИЕ 2: Походит nil
, способный ленивый Вар больше не.
Очень поздно стороне, и даже уверенный, если это будет релевантно в Swift 3, но здесь идет. Ответ David хорош, но если Вы хотите создать многих ленивый способный нолем Вар, необходимо будет записать довольно значительный блок кода. Я пытаюсь создать ADT, который инкапсулирует это поведение. Вот то, что я имею до сих пор:
struct ClearableLazy<T> {
private var t: T!
private var constructor: () -> T
init(_ constructor: () -> T) {
self.constructor = constructor
}
mutating func get() -> T {
if t == nil {
t = constructor()
}
return t
}
mutating func clear() { t = nil }
}
Вы затем объявили бы и использовали бы свойства как это:
var aClient = ClearableLazy(Client.init)
aClient.get().delegate = self
aClient.clear()
существуют вещи, я еще не люблю приблизительно это, но не знаю, как улучшиться:
get()
на свойстве каждый раз Вы хотите использовать его, ужасно. Было бы немного лучше, если бы это было вычисленным свойством, не функцией, но вычислило свойства, не может видоизменяться. get()
, необходимо расширить каждый тип, для которого Вы хотите использовать это с инициализаторами для ClearableLazy
. , Если бы кто-то испытывает желание брать его отсюда, который был бы потрясающим.
Это позволяет устанавливать свойство на nil
для принуждения реинициализации:
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
}
Здесь существуют некоторые хорошие ответы.
Сброс ленивого var действительно, желателен в большом количестве случаев.
я думаю, можно также определить закрытие, чтобы создать клиент и сбросить ленивый var с этим закрытием. Что-то вроде этого:
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
Если цель состоит в том, чтобы повторно инициализировать ленивое свойство, но не обязательно установить, она к нолю, Создающему от Phlippie Bosman и Ben Leggiero, вот что-то, что избегает, чтобы условное выражение проверило каждый раз, когда значение читается:
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()
}
}
Для тестирования:
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"
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
}
}
Вот то, как использовать:
var game = Game()
print(game.scores)
print(game.scores)
game.resetScores()
print(game.scores)
print(game.scores)
Это производит следующий вывод:
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 и Обертка Свойства
@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]
}
Мы сбрасываем кэш путем установки его на любое значение:
var game = Game()
print(game.scores)
print(game.scores)
game.scores = []
print(game.scores)
print(game.scores)