Как написать блок завершения Swift, который можно вызвать только один раз? - PullRequest
0 голосов
/ 28 февраля 2020

Допустим, у меня есть класс Swift, который хранит блок завершения и выполняет несколько асинхронных задач.

Я хочу, чтобы этот блок вызывался той из задач, которая завершается первой, но только эта - я не хочу, чтобы он вызывался снова после завершения второй задачи.

Как я могу реализовать это чистым способом?

Ответы [ 2 ]

0 голосов
/ 28 февраля 2020

Уже есть стандартное выражение однократности. К сожалению, стандарт Objective- C недоступен в Swift (GCD dispatch_once), но стандартная техника Swift работает нормально, а именно свойство с ленивым инициализатором определения и вызова.

Точно так же, как вы делаете это зависит от уровня, на котором вы хотите, чтобы однократность применялась. В этом примере он находится на уровне экземпляра класса:

class MyClass {
    // private part
    private let completion : (() -> ())
    private lazy var once : Void = {
        self.completion()
    }()
    private func doCompletionOnce() {
        _ = self.once
    }
    // public-facing part
    init(completion:@escaping () -> ()) {
        self.completion = completion
    }
    func doCompletion() {
        self.doCompletionOnce()
    }
}

И здесь мы проверим это:

    let c = MyClass() {
        print("howdy")
    }
    c.doCompletion() // howdy
    c.doCompletion()
    let cc = MyClass() {
        print("howdy2")
    }
    cc.doCompletion() // howdy2
    cc.doCompletion()

Если вы продвигаете личные вещи до уровня класса (используя свойство stati c once), завершение может быть выполнено только один раз за время существования всей программы.

0 голосов
/ 28 февраля 2020

До тех пор, пока вам это не нужно для обеспечения безопасности потоков, вы можете решить эту проблему с помощью довольно простого @propertyWrapper.

@propertyWrapper
struct ReadableOnce<T> {
    var wrappedValue: T? {
        mutating get {
            defer { self._value = nil }
            return self._value
        }
        set {
            self._value = newValue
        }
    }
    private var _value: T? = nil
}

Отметьте блок завершения var с помощью @ReadableOnce и он будет уничтожен после первого прочтения его значения.

Примерно так:

class MyClass {
    @ReadableOnce private var completion: ((Error?) -> Void)?

    init(completion: @escaping ((Error?) -> Void)) {
        self.completion = completion
    }

    public func doSomething() {
        // These could all be invoked from different places, like your separate tasks' asynchronous callbacks
        self.completion?(error) // This triggers the callback, then the property wrapper sets it to nil.
        self.completion?(error) // This does nothing
        self.completion?(error) // This does nothing
    }
}

Я написал более подробное обсуждение этого здесь но главное, что нужно знать, это то, что чтение значения устанавливает его в ноль, даже если вы не вызываете замыкание! Это может удивить того, кто не знаком с умным свойством обертка вы написали.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...