programing tip

Swift에서 지연 초기화 된 변수를 다시 초기화합니다.

itbloger 2020. 11. 27. 07:51
반응형

Swift에서 지연 초기화 된 변수를 다시 초기화합니다.


다음과 같이 초기화 된 변수가 있습니다.

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스스로 리셋 할 수있는 방법이 있습니까?


lazy는 명시 적으로 일회성 초기화를위한 것입니다. 채택하려는 모델은 아마도 주문형 초기화 모델 일 것입니다.

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

var _aClient:Client?

이제 _aClientnil이면 초기화되고 반환됩니다. 설정을 통해 다시 초기화 할 수 있습니다._aClient = nil


Swift 4에서 의 동작이 lazy변경 되었기 때문에 저는 struct언어 버전간에 절대 변경되지 않아야하는 매우 구체적인 동작을 제공 하는 몇 가지를 작성했습니다 . 나는 이것을 BH-0-PD 라이센스에 따라 GitHub에 넣었습니다 : https://github.com/RougeWare/Swift-Lazy-Patterns

ResettableLazy

다음은 값을 지연 초기화하고 해당 값을 캐시하고 나중에 지연 재 초기화 할 수 있도록 삭제하는 방법을 제공하는이 질문과 관련된 질문입니다.

이를 위해서는 Swift 5.1이 필요합니다! Swift 4 버전 의 경우 해당 저장소의 버전 1.1.1을 참조하십시오 .

이것의 간단한 사용법은 매우 간단합니다.

@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!

아래 솔루션은 더 이상 Swift 4 이상에서 작동하지 않습니다!

대신 위에 나열된 솔루션 중 하나 또는 @PBosman의 솔루션 을 사용하는 것이 좋습니다.

아래 동작은 Swift 버그 SR-5172 ( PR # 10,911 로 2017-07-14에 해결됨)에 설명 된 버그이며, 이 동작이 의도적이지 않은 것이 분명합니다.

스위프트 3에 대한 해결책은 역사적인 이유로 이하이지만, 버그이기 때문에 그 3.2 이상 스위프트 작동하지 않습니다 악용 난 당신이 않는 것이 좋습니다 하지 이렇게을 :


이것이 언제 추가되었는지 정확히 모르겠지만 Swift 3 부터는 속성을 nil-able로 만들 수 있습니다. 이제 다음 번에 aClient를로 설정 한 후 호출 하면 다시 초기화됩니다. --- 지금은 기술적으로 선택 사항이지만 읽을 때마다 런타임 값이 보장됩니다. 그게 내가 사용하는 이유입니다 항상 안전 호출이며 않을 것이기 때문에, 여기에, 읽을, 그러나 그것은 할 수 있습니다 설정. lazy var aClient:Client! = { var _aClient = Client(ClinetSession.shared()) _aClient.delegate = self return _aClient }()
// ...
aClient = nil
nil!nilnil


편집 : Ben Leggiero의 답변에 따라, 게으른 변수는 nilSwift 3에서 가능합니다. 편집 2 : nil게으른 변수가 더 이상없는 것 같습니다 .

파티에 매우 늦었고 이것이 Swift 3에서 관련이 있는지 확실하지 않지만 여기에 있습니다. David의 대답은 좋지만, 게으른 nil-able 변수를 많이 만들고 싶다면 꽤 많은 코드 블록을 작성해야합니다. 이 동작을 캡슐화하는 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
}

여기에 좋은 답변이 있습니다.
lazy var를 재설정하는 것은 실제로 많은 경우에 바람직합니다.

클라이언트를 생성하고이 클로저로 lazy 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

목적이 lazy 속성을 다시 초기화하는 것이지만 반드시 nil로 설정하지 않아도되는 경우, Building from 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 and 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]
}

We reset the cache by setting it to any value:

var game = Game()
print(game.scores)
print(game.scores)
game.scores = []
print(game.scores)
print(game.scores)

참고URL : https://stackoverflow.com/questions/25072597/re-initialize-a-lazy-initialized-variable-in-swift

반응형