> For the complete documentation index, see [llms.txt](https://wilden-chen.gitbook.io/swift-design-patterns/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wilden-chen.gitbook.io/swift-design-patterns/observer/kvo.md).

# 鍵值觀察 - KVO

在 KVO 中，實體物件可以註冊監聽任何屬性的變化，不管它是否持有該屬性。如果感興趣的話，可以讀一讀[蘋果 KVO 編程指南](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html)。

## 如何使用 KVO

正如前面所提及的， 實體物件可以關注任何屬性的變化。在我們的專案裡面，我們可以用 KVO 關注 `UIImageView` 的 `image` 屬性變化。

在現代 Swift 中，自 Swift 4 起引入了**型別安全且基於閉包的 KVO API**。我們宣告一個 `NSKeyValueObservation` 的可選屬性來管理觀察者的生命週期，不再需要手動重寫 `observeValue(forKeyPath:...)` 與在 `deinit` 中手動移除觀察者。

首先，在 `PhotoView.swift` 宣告一個成員變數：

```swift
private var mImageObservation: NSKeyValueObservation?
```

接著，找到 `override init(frame: CGRect)` 方法，把 `mImageView` 添加到 `subView` 的程式碼後面添加如下程式：

```swift
mImageObservation = mImageView.observe(\.image, options: [.new, .old]) { [weak self] imageView, change in
    self?.mLoadingView.stopAnimating()
}
```

這行程式透過 Swift 的 KeyPath `\.image` 對 `mImageView` 的 `image` 屬性進行了監聽，並且在閉包內執行對應的邏輯（當圖片載入完成時，停止小菊花 loading 畫面）。

因為 `NSKeyValueObservation` 類別在它自己釋放（例如 `PhotoView` 被銷毀而讓其成員變數 `mImageObservation` 隨之釋放）時，會自動移除 KVO 觀察者，因此我們**不再需要**在 `deinit` 中編寫額外的 `removeObserver` 程式碼，也不用複寫複雜的 `observeValue(forKeyPath:of:change:context:)` 方法，這大幅提高了程式碼的安全與可讀性。

在檢測到屬性變化的時候，閉包會自動被調用。在上面的程式碼里，我們在圖片載入完成的時候把那個提示加載的小菊花去掉了。

再次運行項目，你會發現一切正常了：

![](https://2481295995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lejh-llXQZynxNlmsNC%2Fuploads%2Fgit-blob-8e7265dac1aed441f467422b33523a71859c737a%2Fkvo1.png?alt=media)

注意：一定要記得移除觀察者，否則如果物件已經銷毀了還給它發送消息會導致應用閃退。

完成到這一步的Demo：

* 查看原始碼
* [下載ZIP](https://github.com/wildenchen/swift-bethel-of-the-road/tree/eed3c8ff6c1141681b3d3b2c69fac406cd11c24c/DesignPatterns/workspaces/FlickrPhotos-7-KVO.zip)
