- Это ключ-значение наблюдений (кво) доступны в 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 = 0
The
dynamic
модификатор гарантирует, что ссылки на объявление будут динамически отправлены и доступны черезobjc_msgSend
.
В настоящее время Swift не поддерживает встроенный механизм для наблюдения за изменениями свойств объектов, отличных от "self", поэтому нет, он не поддерживает KVO.
однако кво является такой фундаментальной частью Objective-C и Cocoa, что вполне вероятно, что он будет добавлен в будущем. Текущая документация, похоже, подразумевает следующее:
в дополнение к ответу Роба. Этот класс должен наследовать от
NSObject
, и у нас есть 3 способа вызвать изменение свойствиспользовать
setValue(value: AnyObject?, forKey key: String)
СNSKeyValueCoding
class MyObjectToObserve: NSObject { var myDate = NSDate() func updateDate() { setValue(NSDate(), forKey: "myDate") } }
использовать
willChangeValueForKey
иdidChangeValueForKey
СNSKeyValueObserving
class 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)") } }