คุณสามารถใช้ 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 ตอนนี้เรามีการพิมพ์ที่สำคัญของ keypaths โดยใช้อักขระเครื่องหมายทับขวา ( \.bar
คือ keypath สำหรับbar
คุณสมบัติของวัตถุที่กำลังสังเกตเห็น) นอกจากนี้เนื่องจากใช้รูปแบบการปิดเสร็จสิ้นเราไม่จำเป็นต้องลบผู้สังเกตการณ์ด้วยตนเอง (เมื่อtoken
อยู่นอกขอบเขตผู้สังเกตการณ์จะถูกลบสำหรับเรา) และเราไม่ต้องกังวลเกี่ยวกับการเรียกsuper
ใช้งานหากไม่มี การจับคู่. การปิดถูกเรียกก็ต่อเมื่อผู้สังเกตการณ์คนนี้ถูกเรียกใช้ สำหรับข้อมูลเพิ่มเติมโปรดดูที่ WWDC 2017 วิดีโอมีอะไรใหม่ในมูลนิธิ
ใน Swift 3 เพื่อสังเกตสิ่งนี้มันซับซ้อนกว่าเล็กน้อย แต่คล้ายกับสิ่งที่ทำใน Objective-C กล่าวคือคุณจะใช้observeValue(forKeyPath keyPath:, of object:, change:, context:)
ซึ่ง (a) ทำให้แน่ใจว่าเรากำลังเผชิญกับบริบทของเรา (และไม่ใช่สิ่งที่super
อินสแตนซ์ของเราได้ลงทะเบียนเพื่อสังเกต); จากนั้น (b) จัดการหรือส่งต่อไปยังsuper
การนำไปปฏิบัติตามความจำเป็น และให้แน่ใจว่าจะลบตัวคุณเองเป็นผู้สังเกตการณ์เมื่อเหมาะสม ตัวอย่างเช่นคุณอาจลบผู้สังเกตการณ์เมื่อถูกยกเลิกการจัดสรร:
ในสวิฟต์ 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 เท่านั้น ดังนั้นคุณไม่สามารถสังเกตข้อมูลทั่วไป, struct
ประเภทสวิฟต์enum
, ประเภทสวิฟต์เป็นต้น
สำหรับการอภิปรายเกี่ยวกับการใช้งาน Swift 2 ดูคำตอบต้นฉบับของฉันด้านล่าง
การใช้dynamic
คำหลักเพื่อให้บรรลุ KVO ด้วยNSObject
คลาสย่อยนั้นได้อธิบายไว้ในส่วนการสังเกตคีย์ - ค่าของการยอมรับแนวคิดการออกแบบโคโค่ในบทของการใช้ Swift กับโกโก้และวัตถุประสงค์ -C :
การสังเกตค่าคีย์เป็นกลไกที่อนุญาตให้วัตถุได้รับแจ้งถึงการเปลี่ยนแปลงคุณสมบัติที่ระบุของวัตถุอื่น คุณสามารถใช้คีย์ - ค่าการสังเกตด้วยคลาส 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)
}
}
[หมายเหตุการสนทนา KVO นี้ในภายหลังได้ถูกลบออกจากคู่มือการใช้ Swift กับ Cocoa และ Objective-Cซึ่งได้รับการดัดแปลงสำหรับ Swift 3 แต่ก็ยังใช้งานได้ตามที่ระบุไว้ด้านบนของคำตอบนี้]
เป็นที่น่าสังเกตว่า Swift มีระบบสังเกตการณ์คุณสมบัติของตนเองแต่สำหรับคลาสที่ระบุรหัสของตัวเองที่จะดำเนินการเมื่อสังเกตคุณสมบัติของตัวเอง ในทางกลับกัน KVO นั้นถูกออกแบบมาเพื่อลงทะเบียนเพื่อสังเกตการเปลี่ยนแปลงคุณสมบัติบางอย่างของคลาสอื่น ๆ