Преобразование старого KVO в Swift 4 - PullRequest
0 голосов
/ 03 мая 2018

Я пытаюсь преобразовать какой-то старый код Swift WWDC в Swift 4. Я думаю, что у меня все сделано, за исключением этого последнего бита, который делает некоторые KVO. Это было довольно сложно сузить до последнего бита, потому что кажется, что все работает как пример кода - но эти методы KVO не вызываются в Swift 4. Я обнаружил это здесь: Open Radar Bug

Каким будет Swift 4 способ представить следующее?

// использовать механизм KVO, чтобы указать, что изменения «состояния» влияют и на другие свойства

class func keyPathsForValuesAffectingIsReady() -> Set<NSObject> {
    return ["state" as NSObject]
}

class func keyPathsForValuesAffectingIsExecuting() -> Set<NSObject> {
    return ["state" as NSObject]
}

class func keyPathsForValuesAffectingIsFinished() -> Set<NSObject> {
    return ["state" as NSObject]
}

А вот определения переменных из примера:

    override var isReady: Bool {
    switch state {

        case .initialized:
            // If the operation has been cancelled, "isReady" should return true
            return isCancelled

        case .pending:
            // If the operation has been cancelled, "isReady" should return true
            guard !isCancelled else {
                return true
            }

            // If super isReady, conditions can be evaluated
            if super.isReady {
                evaluateConditions()
            }

            // Until conditions have been evaluated, "isReady" returns false
            return false

        case .ready:
            return super.isReady || isCancelled

        default:
            return false
    }
}

override var isExecuting: Bool {
    return state == .executing
}

override var isFinished: Bool {
    return state == .finished
}

Если вам нужно больше кода, пожалуйста, дайте мне знать.

Если это дублирующий вопрос, пожалуйста, ссылку на дубликат здесь. Мне не удалось найти решение.

Ответы [ 3 ]

0 голосов
/ 03 мая 2018

(NS)Operation сильно зависит от NSObject-KVO

Ближайший синтаксис Swift -

@objc private class func keyPathsForValuesAffectingIsReady() -> Set<String> {
    return [#keyPath(state)]
}

@objc private class func keyPathsForValuesAffectingIsExecuting() -> Set<String> {
    return [#keyPath(state)]
}

@objc private class func keyPathsForValuesAffectingIsFinished() -> Set<String> {
    return [#keyPath(state)]
}

Примечание: возможно, вам понадобится сделать state поточно-ориентированным.

0 голосов
/ 03 мая 2018

Основная проблема заключается в том, что KVO создается с использованием Objective-C и использует среду выполнения Objective-C для обнаружения существования keyPathsForValuesAffecting методов. В Swift 4 методы больше не отображаются в Objective-C по умолчанию, если в них не добавлена ​​аннотация @objc. Итак, в двух словах, добавление аннотации @objc, вероятно, решит вашу проблему.

Еще одна вещь, которую я делаю - не является строго необходимой, но она делает код более привлекательным - это объявлять их как статические константы. @objc заставит их подвергаться Objective-C как методам класса, так что все это работает, и это немного чище. Мне также нравится помещать на них private, поскольку они никогда не будут вызываться кодом Swift, и нет смысла загромождать внутренний и / или публичный интерфейс вашего класса.

Вам также необходимо убедиться, что ваше свойство state соответствует KVO, и отправлять уведомления при его изменении. Вы можете сделать это, задав свойство dynamic, которое заставит систему KVO автоматически генерировать для вас вызовы уведомлений, или вы можете вручную вызвать willChangeValue(for:) и didChangeValue(for:) (или строковые версии * 1014). * и didChangeValue(forKey:)) в ваших обработчиках willSet и didSet для свойства.

Наконец, не используйте необработанные строковые ключи в Swift, если вы можете избежать этого. Механизм #keyPath() является предпочтительным способом получения основанных на строках путей к ключам (и для использования, отличного от этих устаревших методов Objective-C, которые должны принимать строки, вы должны использовать новый тип KeyPath, который еще лучше). Если ваше свойство state не является Objective-C-совместимым типом, вы застряли со старыми путями строковых ключей (в этом случае вы будете запускать уведомления в ваших willSet и didSet, как описано в предыдущий абзац). Кроме того, вы можете создать фиктивный объект Any, который отражает ваше свойство state, исключительно для целей KVO.

Итак, как-то так:

@objc private static let keyPathsForValuesAffectingIsReady: Set<String> = [
    #keyPath(state)
]

Теперь свойство state. Если это Objective-C-совместимый тип, это просто:

@objc dynamic var state: ...

Или, если это не так:

@objc var state: SomeNonObjCThing {
    willSet { self.willChangeValue(forKey: "state") }
    didSet { self.didChangeValue(forKey: "state") }
}

OR

@objc private var _stateKVO: Any { return self.state }
var state: SomeNonObjCThing {
    willSet { self.willChangeValue(for: \.stateKVO) }
    didSet { self.didChangeValue(for: \.stateKVO) }
}
// you can now use #keyPath(_stateKVO) in keyPathsForValuesAffecting...
0 голосов
/ 03 мая 2018
  • Члены keyPathsForValuesAffecting… могут быть свойствами вместо методов.
  • Они должны быть объявлены @objc, потому что система KVO обращается к свойствам, используя среду выполнения Objective-C.
  • Свойства должны иметь тип Set<String>.
  • Если вы используете директиву #keyPath, компилятор может сообщить вам, когда вы указали неверный путь к ключу (например, из-за орфографической ошибки или изменения имени свойства).

Таким образом:

@objc class var keyPathsForValuesAffectingIsReady: Set<String> {
    return [#keyPath(state)]
}

@objc class var keyPathsForValuesAffectingIsExecuting: Set<String> {
    return [#keyPath(state)]
}

@objc class var keyPathsForValuesAffectingIsFinished: Set<String> {
    return [#keyPath(state)]
}

Вам также необходимо убедиться, что ваше свойство state объявлено @objc dynamic.

...