KVO淺析

KVO Key-Value Observing 鍵值監(jiān)聽(tīng)

KVO是一個(gè)觀察者模式。觀察一個(gè)對(duì)象的屬性,注冊(cè)一個(gè)指定的路徑,若這個(gè)對(duì)象的的屬性修改,則KVO會(huì)自動(dòng)通知觀察者。

使用步驟

1.注冊(cè)監(jiān)聽(tīng)

/**
     *  注冊(cè)一個(gè)監(jiān)聽(tīng)
     *
     *  @param observer 觀察者
     *  @param keyPath  屬性名字
     *  @param options  屬性的變化
     *  @param context  void類(lèi)型
     */
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

2.回調(diào)函數(shù)

/**
 *  當(dāng)監(jiān)控的某個(gè)屬性的值改變了就會(huì)調(diào)用
 *
 *  @param keyPath 屬性名(哪個(gè)屬性改了?)
 *  @param object  哪個(gè)對(duì)象的屬性被改了?
 *  @param change  屬性的修改情況(屬性原來(lái)的值、屬性最新的值)
 *  @param context void類(lèi)型
 */
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"%@對(duì)象的%@屬性改變了:%@", object, keyPath, change);
}

3.移除觀察,釋放內(nèi)存,在dealloc函數(shù)釋放

/**
 *  移除觀察者
 *
 *  @param observer 觀察者
 *  @param keyPath  觀察的屬性
 */
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7, 5_0);
KVO的觀察者是由兩種模式的。一種是自動(dòng)通知,一種是手動(dòng)通知。

自動(dòng)通知自動(dòng)監(jiān)聽(tīng)對(duì)象的屬性,不管這個(gè)屬性的前后屬性變化的值是否一樣,都會(huì)通知觀察者。
手動(dòng)通知重寫(xiě)willChangeValueForKey:和didChangeValueForKey: 方法通知觀察者。
一般都是用自動(dòng)通知,方便快捷。
下面兩種寫(xiě)法,都會(huì)舉例說(shuō)明。

自動(dòng)通知,主要看監(jiān)聽(tīng)回調(diào)的分析

場(chǎng)景:模擬人年齡的變化,看接受通知的次數(shù)

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person = [[Person alloc]init];
    self.person.age = 15;

    /**
     *  注冊(cè)監(jiān)聽(tīng)
     */
    [self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    /**
     *  第二次賦值 20
     */
    self.person.age = 20;
    /**
     *  再次賦值 20
     */
    self.person.age = 20;
    /**
     *  詳細(xì)見(jiàn)控制臺(tái)輸出,只比較新舊值
     */    
}

/**
 *  監(jiān)聽(tīng)回調(diào)
 */
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    
    NSLog(@"\n change = %@ \n keyPath =%@ object =%@ context=%@",change ,keyPath,object,context);
 
    
    
    /** ============================ 控制臺(tái)輸出 ===================================
     * 
     
     
     change = {
     kind = 1;
     new = 20;        --------->新的值是 20 注冊(cè)監(jiān)聽(tīng)后,observeValueForKeyPath會(huì)回調(diào)輸出
     old = 15;        --------->舊的值是 15 注冊(cè)監(jiān)聽(tīng)前,這個(gè)屬性初始化時(shí)候就是15。
     }
     keyPath =age object =<Person: 0x7feec962a9d0> context=(null)
     
     
     change = {
     kind = 1;
     new = 20;        --------->新的值是 20 再次傳入20,observeValueForKeyPath這個(gè)還是會(huì)回調(diào)輸出
     old = 20;        --------->舊的值是 20
     }
     keyPath =age object =<Person: 0x7feec962a9d0> context=(null)
     
     */
    
    
    /**
     *  由控制臺(tái)輸出結(jié)果得出結(jié)論,只要是對(duì)屬性進(jìn)行改變,不管屬性的值是否變化,不區(qū)分新舊值的變化,observeValueForKeyPath都是回調(diào),通知觀察者的。
     */
    
}


-(void)dealloc{

    /**
     *  移除觀察
     */
    [self.person removeObserver:self forKeyPath:@"age"];
}
手動(dòng)通知

場(chǎng)景:模擬人年齡的變化,看接受通知的次數(shù)
需要在Person類(lèi)里面重寫(xiě)方法,具體看實(shí)現(xiàn)代碼

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person = [[Person alloc]init];
    self.person.age = 15;

    /**
     *  注冊(cè)監(jiān)聽(tīng)
     */
    [self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    /**
     *  第二次賦值 20
     */
    self.person.age = 20;
    /**
     *  再次賦值 20
     */
    self.person.age = 20;
    

    /**
     *  詳細(xì)見(jiàn)控制臺(tái)輸出,只比較新舊值
     */
    
    
    
}



/**
 *  監(jiān)聽(tīng)回調(diào)
 */
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    
    NSLog(@"\n change = %@ \n keyPath =%@ object =%@ context=%@",change ,keyPath,object,context);
 
    
    
    /** ============================ 控制臺(tái)輸出 ===================================
     * 
     
     
     change = {
     kind = 1;
     new = 20;        --------->新的值是 20 注冊(cè)監(jiān)聽(tīng)后,observeValueForKeyPath會(huì)回調(diào)輸出
     old = 15;        --------->舊的值是 15 注冊(cè)監(jiān)聽(tīng)前,這個(gè)屬性初始化時(shí)候就是15。
     }
     keyPath =age object =<Person: 0x7feec962a9d0> context=(null)
     
     */
    
    
    /**
     *  和自動(dòng)通知輸出作對(duì)比,這樣明白理解。主要是在Person類(lèi)里面重寫(xiě)了 automaticallyNotifiesObserversForKey 這個(gè)方法。以及重寫(xiě)age的setter方法
     *  由控制臺(tái)輸出結(jié)果得出結(jié)論,只要監(jiān)聽(tīng)對(duì)象的屬性值前后發(fā)生改變,observeValueForKeyPath就回調(diào),通知觀察者的。
     */
    
}


-(void)dealloc{

    /**
     *  移除觀察
     */
    [self.person removeObserver:self forKeyPath:@"age"];
}
Person類(lèi)的實(shí)現(xiàn)
//
//  Person.m
//  KVO
//
//  Created by XXXXXX on 15/11/9.
//  Copyright ? 2015年 Simon. All rights reserved.
//

#import "Person.h"

@implementation Person
-(void)setAge:(float)age{
    
    /**
     *  判斷兩值是否一樣,一樣就不復(fù)賦值了
     */
    if (_age!= age) {
        [self willChangeValueForKey:@"age"];
        
        _age = age;
        
        [self didChangeValueForKey:@"age"];
    }
}

/**
 *  是否對(duì)這個(gè)key開(kāi)啟自動(dòng)發(fā)送通知
 *
 *  @param theKey 監(jiān)聽(tīng)的屬性key
 *
 *  @return 布爾值
 */
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
    BOOL automatic = NO;
    
    if ([theKey isEqualToString:@"age"])
    {
        automatic = NO;
    }
    else
    {
        automatic=[super automaticallyNotifiesObserversForKey:theKey];
    }
    return automatic;
}
@end

觀察了自動(dòng)通知和手動(dòng)通知,各有所長(zhǎng),看你們喜歡哪個(gè)。記住手動(dòng)通知必須重寫(xiě)方法,只有新舊值前后不一樣才會(huì)通知。自動(dòng)通知就不管新舊值是否一樣,都說(shuō)告訴觀察者。

實(shí)現(xiàn)原理可以參考這個(gè)博客,寫(xiě)的很詳細(xì)。[深入淺出Cocoa]詳解鍵值觀察(KVO)及其實(shí)現(xiàn)機(jī)理][1]
[1]:http://blog.csdn.net/kesalin/article/details/8194240

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 本文結(jié)構(gòu)如下: Why? (為什么要用KVO) What? (KVO是什么) How? ( KVO怎么用) Mo...
    等開(kāi)會(huì)閱讀 1,728評(píng)論 1 21
  • 本文由我們團(tuán)隊(duì)的 糾結(jié)倫 童鞋撰寫(xiě)。 文章結(jié)構(gòu)如下: Why? (為什么要用KVO) What? (KVO是什么...
    知識(shí)小集閱讀 7,483評(píng)論 7 105
  • 上半年有段時(shí)間做了一個(gè)項(xiàng)目,項(xiàng)目中聊天界面用到了音頻播放,涉及到進(jìn)度條,當(dāng)時(shí)做android時(shí)候處理的不太好,由于...
    DaZenD閱讀 3,096評(píng)論 0 26
  • ??KVO簡(jiǎn)介:鍵值觀察,是基于鍵值編碼(KVC)的一種觀察模式。 是iOS中常用的一種消息傳遞機(jī)制。??對(duì)接模...
    西葉lv閱讀 531評(píng)論 0 2
  • Key-Value Observing機(jī)制 知識(shí)點(diǎn)介紹 Key-Value Observing (簡(jiǎn)寫(xiě)為KVO):...
    此生浮華祇盼伊亽閱讀 1,282評(píng)論 0 3

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