もしそうなら、Objective-CでKey-Value観測を使用するときに他に存在しなかった重要な違いはありますか?
.initial
。解決策については、こちらをご覧ください。Appleのドキュメントをご覧になることを強くお勧めします。最近更新され、多くの重要な注意事項が記載されています。Robの他の回答
もしそうなら、Objective-CでKey-Value観測を使用するときに他に存在しなかった重要な違いはありますか?
.initial
。解決策については、こちらをご覧ください。Appleのドキュメントをご覧になることを強くお勧めします。最近更新され、多くの重要な注意事項が記載されています。Robの他の回答
回答:
(新しい情報を追加するために編集):Combineフレームワークを使用すると、KVOを使用するのではなく、望んだことを達成できるかどうかを検討します。
はいといいえ。KVOは、いつものようにNSObjectサブクラスで機能します。NSObjectをサブクラス化しないクラスでは機能しません。Swiftには(現在は少なくとも)独自のネイティブな監視システムはありません。
(他のプロパティをObjCとして公開してKVOが機能するようにする方法については、コメントを参照してください)
完全な例については、Appleのドキュメントを参照してください。
dynamic
、任意のSwiftクラスでキーワードを使用して、KVOサポートを有効にすることができます。
dynamic
キーワードは、Key-Valueを監視可能にするプロパティに適用されます。
dynamic
キーワードを使用します(クラス自体のキーワードではありません)。これは私のために働いた!dynamic
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では、Swiftでのキー値監視の使用で概説されているように、これは大幅に簡素化されています。
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のビデオ、What's New in Foundationを参照してください。
Swift 3では、これを観察するために少し複雑ですが、Objective-Cで行うことと非常によく似ています。つまり、次のいずれかを実装しますobserveValue(forKeyPath keyPath:, of object:, change:, context:)
。(a)コンテキストを処理していることを確認します(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
タイプ、Swift enum
タイプなどを監視することはできません。
Swift 2実装の説明については、以下の元の回答を参照してください。
dynamic
キーワードを使用してNSObject
サブクラスでKVOを実現する方法については、「CocoaおよびObjective-CでのSwiftの使用」の「Cocoa設計規則の採用」の章の「キー値の監視」セクションを参照してください。ガイドください。
キー値監視は、他のオブジェクトの指定されたプロパティへの変更をオブジェクトに通知できるようにするメカニズムです。クラスがクラスから継承する限り、Swiftクラスでキー値監視を使用できます
NSObject
。これらの3つのステップを使用して、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) } }
[注:このKVOの議論は、Swift 3向けに適合された「CocoaおよびObjective-CでのSwiftの使用」ガイドから削除されましたが、この回答の冒頭で概説されているとおりに機能します。]
Swiftには独自のネイティブプロパティオブザーバーシステムがあることは注目に値しますが、それは、独自のプロパティの監視時に実行される独自のコードを指定するクラス用です。一方、KVOは、他のクラスの動的プロパティの変更を監視するように登録するように設計されています。
myContext
複数のプロパティの目的は何ですか?またどのように観察しますか?
context
ポインターを提供することもできます。context
ポインターは、observeValueForKeyPath:ofObject:change:context:
呼び出されたときにオブザーバーに提供されます。context
ポインターは、Cポインターまたはオブジェクト参照にcontext
することができます。監視されている変更を判別するため、またはオブザーバーに他のデータを提供するための一意の識別子として使用されます。」
options
空のままにした場合、それは単ににchange
古い値または新しい値が含まれないことを意味します(たとえば、オブジェクト自体を参照することによって、新しい値を自分で取得する場合があります)。だけを指定し.new
、を指定しない場合は、新しい値のみが含まれ、古い値は含まれない.old
ことを意味しchange
ます(たとえば、多くの場合、古い値が何であるかは気にせず、新しい値のみが気になります)。observeValueForKeyPath
古い値と新しい値の両方を渡す必要がある場合は、を指定します[.new, .old]
。一番下の行は、options
何がchange
辞書に含まれるかを指定するだけです。
はいといいえの両方:
はい、Swiftで同じ古いKVO APIを使用してObjective-Cオブジェクトを監視できます。から継承するSwiftオブジェクトのプロパティを
確認することもできdynamic
ますNSObject
。
しかし... いいえ、強く型付けされていません。Swiftネイティブの観測システムがそうであると期待できるからです。
CocoaおよびObjective-CでのSwiftの使用| キーバリュー観察
いいえ、現在、任意のSwiftオブジェクトの組み込みの値監視システムはありません。
はい、組み込みのプロパティオブザーバーがあり、厳密に型指定されています。
しかし... いいえ、オブジェクトのプロパティの監視のみを許可し、ネストされた監視(「キーパス」)をサポートしていないため、KVOではありません。明示的に実装する必要があります。
Swiftプログラミング言語| プロパティオブザーバー
はい、明示的に型指定された明示的な値監視を実装し、他のオブジェクトから複数のハンドラーを追加でき、ネスト/「キーパス」もサポートできます。
しかし... いいえ、監視可能として実装したプロパティに対してのみ機能するため、KVOにはなりません。
このような値の監視を実装するためのライブラリは、次の場所にあります
。Observable-Swift-KVO for 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)")
}
ロブの答えに加えて。そのクラスは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タイプの互換性を参照してください
メソッドの実装を動的に置き換えるキー値監視などのAPIを使用している場合は、動的修飾子を使用して、Objective-Cランタイムを通じてメンバーへのアクセスを動的にディスパッチすることを要求できます。
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
また、プロパティのゲッターとセッターは使用時に呼び出されます。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
}
}
}
またはを使用しCombine
なくても使用NSObject
できますObjective-C
可用性: iOS 13.0+
、macOS 10.15+
、tvOS 13.0+
、watchOS 6.0+
、Mac Catalyst 13.0+
、Xcode 11.0+
注:値型ではなくクラスでのみ使用する必要があります。
Swiftバージョン:5.1.2
import Combine //Combine Framework
//Needs to be a class doesn't work with struct and other value types
class Car {
@Published var price : Int = 10
}
let car = Car()
//Option 1: Automatically Subscribes to the publisher
let cancellable1 = car.$price.sink {
print("Option 1: value changed to \($0)")
}
//Option 2: Manually Subscribe to the publisher
//Using this option multiple subscribers can subscribe to the same publisher
let publisher = car.$price
let subscriber2 : Subscribers.Sink<Int, Never>
subscriber2 = Subscribers.Sink(receiveCompletion: { print("completion \($0)")}) {
print("Option 2: value changed to \($0)")
}
publisher.subscribe(subscriber2)
//Assign a new value
car.price = 20
Option 1: value changed to 10
Option 2: value changed to 10
Option 1: value changed to 20
Option 2: value changed to 20
現在、Swiftは 'self'以外のオブジェクトのプロパティ変更を監視するための組み込みメカニズムをサポートしていないため、KVOはサポートされていません。
ただし、KVOはObjective-CおよびCocoaの非常に重要な部分であるため、将来的に追加される可能性が非常に高いようです。現在のドキュメントはこれを暗示しているようです:
Key-Value監視
今後の情報。
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でこの方法でKVOを使用していました。このコードは、ほとんど変更することなく使用できます。
Intなどの型で問題が発生した場合のもう1つの例は?そして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)")
}
}