[iOS]KVOController踩坑記

weixin_34320159發表於2017-12-15

KVO 作為 iOS 中一種強大並且有效的機制,為我們檢測物件屬性的變化提供了幫助;

但系統提供的KVO介面實在太麻煩,所以開發過程中,我們使用了facebook 開源的KVOController

今天和大家分享一下在專案開發過程中KVOController的填坑經歷。

第一個坑:

1339727-eb018fee80a1d131.png

這樣使用會導致selfKVOController形成迴圈引用。

1. KVOController為所有的NSObject物件都提供 -KVOController 屬性,所以這裡self是強引用了KVOController

1339727-e334c8d13ddae036.png

2.     FBKVOController 例項方法-observer:keyPath:options:block: 呼叫後會強引用 self 

1339727-4e0240e5e69e5ade.png
1339727-430c3a1d81a075dc.png

[_objectInfosMap setObject:infos forKey:object];

這裡的 object 就是 -[KVOController observer:keyPath:options:block:] 傳進來的第一個引數,也就是"self"

也就是把 “self” 作為 key,儲存在FBKVOController的私有變數_objectInfosMap裡面

就會形成 “self ----》 KVOController ----》 self” 這樣的迴圈引用


第二個坑:

為了解決上面的迴圈引用問題,我把程式碼改成了下面醬紫,iOS10必崩


1339727-a7560290f276b823.png

KVOControllerNonRetainingKVOController 的區別就是初始化的時候,retainObservedNO

1339727-5e336669bd0117fc.png
1339727-fc0c2d2a99dba66d.png

上面“坑一”說到,“self” 是被 _objectInfosMap 強引用了,retainObserved 傳 NO,就可以解決這個問題

但是事情並沒有這樣結束。接下來的測試,ios10的系統必崩


1339727-f8f5f60fdea7320c.png

這是 "self" 釋放了,但是沒有呼叫到 removeObserver 的崩潰棧

-[FBKVOController _unobserveAll] 裡面除錯,發現 _objectInfosMap 中已經沒有了 “self”

呼叫順序是這樣的:

1. [self dealloc]

2. [FBKVOController dealloc]

3. [FBKVOController dealloc] 裡面找存起來的"self",並呼叫 [self removeObserver] 

因為第3步中,“self”已經被釋放,FBKVOController 中對“self” weak 指標已經置 nil

所以最終並沒有呼叫到 “self” 的 removeObserver

1339727-e1f895cf903dbd1d.png


總結:

一、物件監聽自己的屬性變化,應該在setter中監聽;而不應該使用 -[self.KVOController observe:self]

二、物件dealloc時,指向物件的weak指標已經置為nil