在Swift中重新初始化一个惰性初始化变量

Cai*_*Cai 63 swift

我有一个初始化为的变量:

lazy var aClient:Clinet = {
    var _aClient = Clinet(ClinetSession.shared())
    _aClient.delegate = self
    return _aClient
}()
Run Code Online (Sandbox Code Playgroud)

问题是,在某些时候,我需要重置此aClient变量,以便它可以在ClinetSession.shared()更改时再次初始化.但是如果我将类设置为可选Clinet?,当我尝试将其设置为时,LLVM会给我一个错误nil.如果我只是在代码中的某处使用aClient = Clinet(ClinetSession.shared())它重置它,它将最终得到EXEC_BAD_ACCESS.

有没有办法可以使用lazy并被允许重置自己?

谢谢!

Dav*_*rry 86

lazy明确表示只进行一次初始化.您要采用的模型可能只是初始化按需模型:

var aClient:Client {
    if(_aClient == nil) {
        _aClient = Client(ClientSession.shared())
    }
    return _aClient!
}

var _aClient:Client?
Run Code Online (Sandbox Code Playgroud)

现在,只要_aClientnil,它会被初始化并返回.它可以通过设置重新初始化_aClient = nil

  • 我曾希望在Swift 2中有更好的东西.你真正想要的是一个带有setter的var,它只允许设置为nil,而getter代码只在属性为nil时调用.像var aClient:Client {setnil; getnil {aClient = Client(ClientSession.shared())}}如果在将变量设置为nil后得到变量,则会调用getnil. (6认同)

Ben*_*ero 42

因为lazy在Swift 4 中改变了行为,我写了几个struct给出非常具体的行为,这些行为在语言版本之间永远不会改变.这些是在GitHub上的ResettableLazy许可证下:https://github.com/BenLeggiero/Swift-Lazy-Patterns

lazy var aClient:Client! = { var _aClient = Client(ClinetSession.shared()) _aClient.delegate = self return _aClient }()
// ...
aClient = nil

以下是与此问题相关的问题,它为您提供了一种方法来延迟初始化值,缓存该值并将其销毁,以便以后可以进行延迟重新初始化:

@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!"
Run Code Online (Sandbox Code Playgroud)

这可以这样使用:

Hello, lazy!
Hello, lazy!
Hello, lazy!
Hello, lazy!
Overwritten
Hello, lazy!
Run Code Online (Sandbox Code Playgroud)

这将打印:

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!"
Run Code Online (Sandbox Code Playgroud)

这种模式的一个主要优点是它可以存储nil得很好,而其他模式在这里并没有真正定义的行为:

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!"
Run Code Online (Sandbox Code Playgroud)

这将打印:

Initializer side-effect
Hello, lazy!
Hello, lazy!
Initializer side-effect
Hello, lazy!
Hello, lazy!
Overwritten
Initializer side-effect
Hello, lazy!
Run Code Online (Sandbox Code Playgroud)

以下解决方案不再适用于Swift 4!

相反,我建议您使用上面列出的解决方案之一,或@ PBosman的解决方案

以下行为是一个错误,在Swift bug SR-5172中描述(已经在2017-07-14与PR#10,911一起解决),很明显这种行为从来都不是故意的.

Swift 3的解决方案由于历史原因而在下面,但是因为它是一个在Swift 3.2+中不起作用的漏洞利用,我建议你要这样做:


我不确定这个添加的确切时间,但是从Swift 3开始,你可以简单地使属性为nil-able: 现在,下次在设置之后调用aClient时,它将被重新初始化.---请注意,虽然现在技术上是可选的,但每次尝试读取它时,都保证具有运行时值.这就是为什么我使用,在这里,因为它始终是一个安全的通话,绝不会被为,但它可以被设置到.!nilnillazystruct

  • 恕我直言,这是一个比接受的答案更清洁的方式. (2认同)

Phl*_*man 6

编辑:根据Ben Leggiero的回答,懒惰的变量可以nil在Swift 3中编辑.编辑2:看起来像懒惰的变量就不复存在nil了.

非常迟到了,甚至不会知道这将是斯威夫特3有关,但在这里不用.大卫的答案很好,但如果你想创建许多懒惰的无法变量,你将不得不写一个相当庞大的代码块.我正在尝试创建一个封装此行为的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 }
}
Run Code Online (Sandbox Code Playgroud)

然后,您将声明并使用如下属性:

var aClient = ClearableLazy(Client.init)
aClient.get().delegate = self
aClient.clear()
Run Code Online (Sandbox Code Playgroud)

有些事情我还不喜欢这个,但不知道如何改进:

  • 你必须将构造函数传递给初始化程序,它看起来很难看.但是,它的优点是可以准确指定如何创建新对象.
  • get()每次想要使用它时都要打电话是很糟糕的.如果这是一个计算属性而不是一个函数,那会好一点,但计算属性不能变异.
  • 为了消除调用的需要get(),你必须扩展你想要使用它的每个类型的初始化器ClearableLazy.

如果有人想从这里捡起它,那就太棒了.


Wil*_*ken 5

这允许将属性设置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
}
Run Code Online (Sandbox Code Playgroud)