聊聊 KVO

引用:http://khanlou.com/2013/12/kvo-considered-harmful/

細(xì)數(shù)KVO的弊端:

  1. 所有實(shí)現(xiàn)都在同一個(gè)方法里調(diào)用
 - (void)addObserver: (NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context 

比如說(shuō)我需要觀察tablview的contentsize屬性,我這樣來(lái)寫:

 [_tableView addObserver:self forKeyPath:@"contentSize" options:0 context:NULL];  

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {  
    [self configureView];  
}  

完成了?看起來(lái)很簡(jiǎn)單啊,so young,為了寫這個(gè),我們還需要做些額外的工作 orz

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {  
    if (object == _tableView && [keyPath isEqualToString:@"contentSize"]) {  
        [self configureView];  
    }  
} 
  1. KVO is string-ly typed
    keypath ‘contentSiz’ 是字符串類型的,這就意味著編譯器跟解析器不會(huì)告訴你這個(gè)屬性是什么類型的或者存不存在。它只是一個(gè)字符串。我們只能使用NSStringFromSelector(@selector(contentSize))來(lái)讓編譯器告訴我們這個(gè)存不存在。
    另外,當(dāng)我們觀察一個(gè)view controller時(shí),想要獲取它scrollview的contentsize,這時(shí)候keypathscrollview.contentOffset。這種情況下我們能做的事就更少了。
  2. KVO 必須處理父類實(shí)現(xiàn)
    我們也許還有一個(gè)父類也在監(jiān)聽,并實(shí)現(xiàn)了這個(gè)接口方法,那我們就該調(diào)用super方法
    if (object == _tableView && [keyPath isEqualToString:@"contentSize"]) {  
        [self configureView];  
    } else {  
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];  
    }  
} 

如果我們一不小心忘了,那就可能導(dǎo)致父類監(jiān)聽失效。
所以保險(xiǎn)起見,我們最好不嫌麻煩地調(diào)用super方法 orz。

  1. KVO 在解除注冊(cè)的時(shí)候可能導(dǎo)致crash
    我們一般會(huì)在-dealloc[_tableView removeObserver:self forKeyPath:NSStringFromSelector(@selector(contentSize)) context:NULL];
    但值得注意的是,tableview有可能會(huì)dealloc兩次,這種情況下因?yàn)槲覀儑L試remove同一個(gè)observance兩次,就可能導(dǎo)致我們的app crash掉。另外,當(dāng)父類也在監(jiān)聽同一屬性的時(shí)候,也可能會(huì)調(diào)用兩次導(dǎo)致crash。
    這個(gè)時(shí)候是該用到 context ,我們可能會(huì)context 當(dāng)做 self來(lái)用,但在父類里,就不好用了。 (因?yàn)閟elf不管在父類還是子類中都會(huì)指向同一個(gè)對(duì)象)所以推薦使用靜態(tài)指針來(lái)存儲(chǔ)context。
static void *ClassNameTableViewContentSizeContext = &ClassNameTableViewContentSizeContext;  
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {  
   if (context == ClassNameTableViewContentSizeContext) {  
       [self doThing];  
   } else if (context == OtherContext) {  
       [self doOtherThing];  
   } else {  
       [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];  
   }  
}  
  1. KVO太難調(diào)試
    delegate調(diào)試時(shí),很容易就追蹤到設(shè)置它的對(duì)象了,但KVO則可能需要在運(yùn)行時(shí)使用 isKindOfClass:來(lái)追蹤。
    這個(gè)就不用多舉例子了,用過的人都有體會(huì)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 寫在前面 程序設(shè)計(jì)語(yǔ)言中有各種各樣的設(shè)計(jì)模式(pattern)和與此對(duì)應(yīng)的反設(shè)計(jì)模式(anti-pattern),...
    Frankxp閱讀 5,020評(píng)論 0 23
  • 先聊聊 KVO 與 KVC 的區(qū)別吧:KVO是指鍵-值-觀察者模式, 鍵值監(jiān)聽, 監(jiān)聽一個(gè)對(duì)象屬性值的改變。KVO...
    smile麗語(yǔ)閱讀 416評(píng)論 1 3
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,106評(píng)論 0 9
  • 上半年有段時(shí)間做了一個(gè)項(xiàng)目,項(xiàng)目中聊天界面用到了音頻播放,涉及到進(jìn)度條,當(dāng)時(shí)做android時(shí)候處理的不太好,由于...
    DaZenD閱讀 3,109評(píng)論 0 26
  • 夜風(fēng)似影久等在門外 殘?jiān)陋?dú)掛在窗臺(tái) 將回憶映白 懷那人還在不在 繁華的塵埃落下來(lái) 喧囂散去忘記了悲哀 用一生緣分等...
    花開淺夏閱讀 557評(píng)論 0 1

友情鏈接更多精彩內(nèi)容