Bạn có thể sử dụng KVO trong Swift, nhưng chỉ cho dynamic
các thuộc tính của NSObject
lớp con. Hãy xem xét rằng bạn muốn quan sát bar
tài sản của một Foo
lớp. Trong Swift 4, chỉ định bar
là dynamic
thuộc tính trong NSObject
lớp con của bạn :
class Foo: NSObject {
@objc dynamic var bar = 0
}
Sau đó bạn có thể đăng ký để quan sát các thay đổi đối với bar
tài sản. Trong Swift 4 và Swift 3.2, điều này đã được đơn giản hóa rất nhiều, như được nêu trong Sử dụng Quan sát giá trị khóa trong 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)")
}
}
}
Lưu ý, trong Swift 4, giờ đây chúng ta đã gõ mạnh các phím bấm bằng cách sử dụng ký tự dấu gạch chéo ngược ( \.bar
là đường dẫn phím cho thuộc bar
tính của đối tượng được quan sát). Ngoài ra, vì sử dụng mẫu đóng hoàn thành, chúng tôi không phải xóa thủ công (khi token
rơi ra khỏi phạm vi, người quan sát sẽ bị xóa đối với chúng tôi) và chúng tôi không phải lo lắng về việc gọi super
triển khai nếu khóa không trận đấu. Việc đóng cửa chỉ được gọi khi người quan sát cụ thể này được gọi. Để biết thêm thông tin, hãy xem video WWDC 2017, Có gì mới trong Foundation .
Trong Swift 3, để quan sát điều này, nó phức tạp hơn một chút, nhưng rất giống với những gì người ta làm trong Objective-C. Cụ thể, bạn sẽ triển khai observeValue(forKeyPath keyPath:, of object:, change:, context:)
(a) đảm bảo rằng chúng tôi xử lý bối cảnh của chúng tôi (và không phải là điều mà super
cá thể của chúng tôi đã đăng ký để quan sát); và sau đó (b) hoặc xử lý nó hoặc chuyển nó cho việc super
thực hiện, khi cần thiết. Và hãy chắc chắn để loại bỏ bản thân như một người quan sát khi thích hợp. Ví dụ: bạn có thể xóa trình quan sát khi nó bị hủy:
Trong 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])")
}
}
Lưu ý, bạn chỉ có thể quan sát các thuộc tính có thể được biểu diễn trong Objective-C. Do đó, bạn không thể quan sát thuốc generic, struct
loại Swift enum
, loại Swift , v.v.
Để thảo luận về việc triển khai Swift 2, hãy xem câu trả lời ban đầu của tôi, bên dưới.
Sử dụng dynamic
từ khóa để đạt được KVO với các NSObject
lớp con được mô tả trong phần Quan sát giá trị khóa của chương Các quy ước thiết kế ca cao thông qua của Hướng dẫn sử dụng Swift với ca cao và mục tiêu-C :
Quan sát giá trị khóa là một cơ chế cho phép các đối tượng được thông báo về các thay đổi đối với các thuộc tính được chỉ định của các đối tượng khác. Bạn có thể sử dụng quan sát khóa-giá trị với một lớp Swift, miễn là lớp kế thừa từ NSObject
lớp đó. Bạn có thể sử dụng ba bước này để triển khai quan sát khóa-giá trị trong Swift.
Thêm công cụ dynamic
sửa đổi vào bất kỳ tài sản nào bạn muốn quan sát. Để biết thêm thông tin về dynamic
, xem Yêu cầu công văn động .
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
Tạo một biến bối cảnh toàn cầu.
private var myContext = 0
Thêm một trình quan sát cho đường dẫn khóa và ghi đè observeValueForKeyPath:ofObject:change:context:
phương thức và xóa trình quan sát trong 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)
}
}
[Lưu ý, cuộc thảo luận KVO này sau đó đã bị xóa khỏi hướng dẫn Sử dụng Swift với Ca cao và Mục tiêu-C , đã được điều chỉnh cho Swift 3, nhưng nó vẫn hoạt động như được nêu ở đầu câu trả lời này.]
Điều đáng chú ý là Swift có hệ thống quan sát thuộc tính riêng của mình , nhưng đó là cho một lớp chỉ định mã riêng của nó sẽ được thực hiện khi quan sát các thuộc tính của chính nó. Mặt khác, KVO được thiết kế để đăng ký để quan sát các thay đổi đối với một số thuộc tính động của một số lớp khác.