面試中被問到KVO下常見的crash原因该抒。轉(zhuǎn)載了一下KVO使用陷阱
鑒于我自己對這塊沒有太多的認(rèn)知。通過博主文章加深理解~。本文意在探究健壯的KVO實現(xiàn)方案相叁。
在初始化方法中加入:
[_tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
執(zhí)行默認(rèn)回調(diào)
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { }
在dealloc中移除
[_tableView removeObserver:self forKeyPath:@"contentOffset" context:nil];
我們普通寫的代碼可能就這樣就完事了遵绰。甚至在dealloc中我們都不會進行removeObserve的操作。
事實上這樣還遠遠不夠增淹。比如我的一個VC中有多個監(jiān)聽的話椿访,這樣肯定是不行的
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (object == _tableView && [keyPath isEqualToString:@"contentOffset"]) {
[self doSomethingWhenContentOffsetChanges];
}
}
這樣加入判斷則 是哪個造成對象,然后觸發(fā)響應(yīng)虑润。
但是這樣還是不夠的成玫,因為可能當(dāng)前類的父類也響應(yīng)KVO。如果這么搞的話拳喻。KVO會在子類中斷
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (object == _tableView && [keyPath isEqualToString:@"contentOffset"]) {
[self doSomethingWhenContentOffsetChanges];
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
在else的情況里面響應(yīng)super的KVO.順著響應(yīng)鏈條去尋找哭当。
這樣仍然沒有結(jié)束,潛在的問題是可能出現(xiàn)dealloc中KVO的注銷冗澈。KVO的一種缺陷(其實不能稱為缺陷钦勘,應(yīng)該稱為特性)是,當(dāng)對同一個keypath進行兩次removeObserver時會導(dǎo)致程序crash亚亲,這種情況常常出現(xiàn)在父類有一個kvo彻采,父類在dealloc中remove了一次,子類又remove了一次的情況下捌归。這種情況下context始終為空肛响。 這個時候我們可以在父類在子類中的context 定義不同的名稱。這樣的話惜索,可以進行區(qū)分防止removeObserve發(fā)生2次特笋。