- Это ключ-значение наблюдений (кво) доступны в Swift?
Если да, то существуют ли какие-либо ключевые различия, которые не присутствовали при использовании наблюдения за ключевыми значениями в Objective-C?
10 ответов:
да и нет. Кво работает на подклассах NSObject так же, как и всегда. Это не работает для классов, которые не подкласс NSObject. Swift не имеет (по крайней мере, в настоящее время) собственной собственной системы наблюдения.
(см. комментарии о том, как выставлять другие свойства в качестве ObjC, чтобы KVO работал над ними)
посмотреть Документация Apple для полного примера.
вы можете использовать KVO в Swift, но только для
dynamicсвойстваNSObjectподкласс. Считайте, что вы хотели наблюдатьbarсвойстваFooкласса. В Swift 4 укажитеbarкакdynamicсвойстваNSObjectподкласс:class Foo: NSObject { @objc dynamic var bar = 0 }затем вы можете зарегистрироваться, чтобы наблюдать изменения
barсобственность. В Swift 4 и Swift 3.2, это было значительно упрощено:class MyObject { private var token: NSKeyValueObservation var objectToObserve = Foo() init() { token = objectToObserve.observe(\.bar) { [weak self] object, change in // the `[weak self]` is to avoid strong reference cycle; obviously, if you don't reference `self` in the closure, then `[weak self]` is not needed print("bar property is now \(object.bar)") } } }Примечание, в Swift 4, Теперь у нас есть сильный тип клавиш с помощью символ обратной косой черты (
\.bar- это путь кbarсвойства объекта). Кроме того, поскольку он использует шаблон закрытия завершения, нам не нужно вручную удалять наблюдателей (когдаtokenвыпадает из области видимости, наблюдатель удаляется для нас) и нам не нужно беспокоиться о вызовеsuperреализации, если ключ не совпадает. Замыкание вызывается только при вызове этого конкретного наблюдателя. Для получения дополнительной информации см. видео WWDC 2017, что нового в Foundation.в Swift 3, чтобы наблюдать это, это немного сложнее, но очень похоже на то, что вы делаете в Objective-C. А именно, вы бы реализовали
observeValue(forKeyPath keyPath:, of object:, change:, context:)который (А) гарантирует, что мы имеем дело с нашим контекстом (а не что-то, что нашиsuperэкземпляр зарегистрировался для наблюдения); а затем (b) либо обработайте его, либо передайте егоsuperвнедрение, по мере необходимости. И не забудьте удалить себя в качестве наблюдателя, когда это необходимо. Для например, вы можете удалить наблюдателя, когда он освобождается:В Swift 3:
class MyObject: NSObject { private var observerContext = 0 var objectToObserve = Foo() override init() { super.init() objectToObserve.addObserver(self, forKeyPath: #keyPath(Foo.bar), options: [.new, .old], context: &observerContext) } deinit { objectToObserve.removeObserver(self, forKeyPath: #keyPath(Foo.bar), context: &observerContext) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard context == &observerContext else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } // do something upon notification of the observed object print("\(keyPath): \(change?[.newKey])") } }обратите внимание, вы можете наблюдать только свойства, которые могут быть представлены в Objective-C. Таким образом, вы не можете наблюдать дженерики, Swift
structтипы, Swiftenumтипы, etc.для обсуждения реализации Swift 2 см. мой оригинальный ответ ниже.
С помощью
dynamicключевое слово для достижения кво сNSObjectподклассы описаны в элемент на Принятие Конвенций По Дизайну Какао глава использование Swift с какао и Objective-C руководство:key-value observing-это механизм, который позволяет уведомлять объекты об изменениях заданных свойств других объектов. Вы можете использовать наблюдение за значением ключа с помощью класса Swift, если класс наследует от
NSObjectкласса. Вы можете использовать эти три шага для осуществления ключ-значение наблюдения в Swift.
добавить
dynamicмодификатор для любого свойства, которое вы хотите наблюдать. Для получения дополнительной информации оdynamicсм. Требуется Динамическая Отправка.class MyObjectToObserve: NSObject { dynamic var myDate = NSDate() func updateDate() { myDate = NSDate() } }создать глобальную переменную контекста.
private var myContext = 0добавьте наблюдателя для пути ключа и переопределите
observeValueForKeyPath:ofObject:change:context:метод, и удалить наблюдателя вdeinit.class MyObserver: NSObject { var objectToObserve = MyObjectToObserve() override init() { super.init() objectToObserve.addObserver(self, forKeyPath: "myDate", options: .New, context: &myContext) } override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) { if context == &myContext { if let newValue = change?[NSKeyValueChangeNewKey] { print("Date changed: \(newValue)") } } else { super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context) } } deinit { objectToObserve.removeObserver(self, forKeyPath: "myDate", context: &myContext) } }[обратите внимание, что это обсуждение кво впоследствии было удалено из использование Swift с какао и Objective-C руководство, которое было адаптировано для Swift 3, но оно все еще работает, как указано в верхней части этого ответа.]
стоит отметить, что Swift имеет свой собственный уроженца наблюдатель собственность система, но это для класса, указывающего свой собственный код, который будет выполняться при наблюдении за его собственными свойствами. Кво на с другой стороны, предназначен для регистрации, чтобы наблюдать изменения в каком-то динамическом свойстве какого-то другого класса.
и да и нет:
да, вы можете использовать те же старые API KVO в Swift для наблюдения объектов Objective-C.
Вы также можете наблюдатьdynamicсвойства объектов Swift, наследуемых отNSObject.
Но... нет это не сильно типизировано, как вы могли бы ожидать, что Swift native observation system будет.
использование Swift с какао и Objective-C / Key Value Observingнет, в настоящее время нет встроенной системы наблюдения значений для произвольных объектов Swift.
да, есть строение -Наблюдатели За Недвижимостью, которые строго типизированы.
Но... нет они не являются кво, так как они позволяют только наблюдать за собственными свойствами объектов, не поддерживают вложенные наблюдения ("ключевые пути"), и вы должны явно реализовать их.
Язык Программирования Swift / Обозреватели Свойствда, вы можете реализовать явное наблюдение значений, которое будет строго типизировано и позволит добавлять несколько обработчиков из других объектов и даже поддерживать вложенность / "ключевые пути".
Но... нет это не будет кво, так как он будет работать только для свойств, которые вы реализуете как наблюдаемые.
Вы можете найти библиотеку для реализации такого значения наблюдая здесь:
наблюдаемые-Свифт - код для оперативного наблюдения за значением и события
пример может немного помочь здесь. Если у меня есть экземпляр
modelклассаModelС атрибутамиnameиstateЯ могу наблюдать те атрибуты:let options = NSKeyValueObservingOptions([.New, .Old, .Initial, .Prior]) model.addObserver(self, forKeyPath: "name", options: options, context: nil) model.addObserver(self, forKeyPath: "state", options: options, context: nil)изменения этих свойств вызовут вызов:
override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: NSDictionary!, context: CMutableVoidPointer) { println("CHANGE OBSERVED: \(change)") }
да.
KVO требует динамической отправки, поэтому вам просто нужно добавить
dynamicмодификатор метода, свойства, индекса или инициализатора:
dynamic var foo = 0The
dynamicмодификатор гарантирует, что ссылки на объявление будут динамически отправлены и доступны черезobjc_msgSend.
В настоящее время Swift не поддерживает встроенный механизм для наблюдения за изменениями свойств объектов, отличных от "self", поэтому нет, он не поддерживает KVO.
однако кво является такой фундаментальной частью Objective-C и Cocoa, что вполне вероятно, что он будет добавлен в будущем. Текущая документация, похоже, подразумевает следующее:
в дополнение к ответу Роба. Этот класс должен наследовать от
NSObject, и у нас есть 3 способа вызвать изменение свойствиспользовать
setValue(value: AnyObject?, forKey key: String)СNSKeyValueCodingclass MyObjectToObserve: NSObject { var myDate = NSDate() func updateDate() { setValue(NSDate(), forKey: "myDate") } }использовать
willChangeValueForKeyиdidChangeValueForKeyСNSKeyValueObservingclass MyObjectToObserve: NSObject { var myDate = NSDate() func updateDate() { willChangeValueForKey("myDate") myDate = NSDate() didChangeValueForKey("myDate") } }использовать
dynamic. Смотрите Совместимость Типа Swiftвы также можете использовать динамический модификатор, чтобы требовать динамической отправки доступа к членам через среду выполнения Objective-C, если вы используете API, такие как key-value, которые динамически заменяют реализацию метода.
class MyObjectToObserve: NSObject { dynamic var myDate = NSDate() func updateDate() { myDate = NSDate() } }и свойство getter и setter вызывается при использовании. Вы можете проверить при работе с KVO. Это пример вычисляемого свойства
class MyObjectToObserve: NSObject { var backing: NSDate = NSDate() dynamic var myDate: NSDate { set { print("setter is called") backing = newValue } get { print("getter is called") return backing } } }
важно отметить, что после обновления вашего Xcode до бета 7 возможно, вы получаете следующее сообщение: "метод не переопределяет ни один метод из своего суперкласса". Это из-за опциональности аргументов. Убедитесь, что ваш обработчик наблюдений выглядит точно так:
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [NSObject : AnyObject]?, context: UnsafeMutablePointer<Void>)
Это может оказаться полезным для немногих людей -
// MARK: - KVO var observedPaths: [String] = [] func observeKVO(keyPath: String) { observedPaths.append(keyPath) addObserver(self, forKeyPath: keyPath, options: [.old, .new], context: nil) } func unObserveKVO(keyPath: String) { if let index = observedPaths.index(of: keyPath) { observedPaths.remove(at: index) } removeObserver(self, forKeyPath: keyPath) } func unObserveAllKVO() { for keyPath in observedPaths { removeObserver(self, forKeyPath: keyPath) } } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if let keyPath = keyPath { switch keyPath { case #keyPath(camera.iso): slider.value = camera.iso default: break } } }я использовал кво таким образом в Swift 3. Вы можете использовать этот код с небольшими изменениями.
еще один пример для тех, кто сталкивается с проблемой с такими типами, как Int? и CGFloat?. Вы просто устанавливаете класс как подкласс NSObject и объявляете свои переменные следующим образом, например:
class Theme : NSObject{ dynamic var min_images : Int = 0 dynamic var moreTextSize : CGFloat = 0.0 func myMethod(){ self.setValue(value, forKey: "\(min_images)") } }