天天看點

KVO(鍵值監聽)詳解

iOS通常會把應用程式元件分開成資料模型元件和視圖元件,其中資料模型負責維護應用程式的狀态資料,而視圖元件負責顯示資料模型元件内部的狀态資料。對于上面的設計模式,如果程式存在的需求是:在資料模型元件的狀态資料發生改變時,試圖元件能動态的更新自己,及時顯示資料模型元件更新後的資料。為了解決上面的需求,及時顯示資料模型元件更新後的資料:

1、我們可以用通知中心,但是對于資料模型元件和視圖模型元件之間都需要與iOS的消息中心耦合,而且每當資料模型元件的内部狀态發生改變時,都需要向iOS的消息中心發送消息,這也是非常令人悲哀的事情。

2、那麼如果我們用KVO那麼就是很好的解決方案了,(鍵值監聽機制)

下方方法:

addObserver: forKeyPath: options: context: 注冊一個監聽器用于監聽指定的key路徑

removeObserver: forKeyPath: 為key路徑删除指定的監聽器

removeObserver: forKeyPath: context:為key路徑删除指定的監聽器.隻是多了一個context參數

 當資料模型元件的key路徑對應屬性值發生改變時,作為監聽器的視圖元件将會被激發,假發是就會回調監聽器自身的監聽方法,該監聽方法如下:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

{

}

由此可見,作為監聽器的試圖元件需要重寫上方這個方法,重寫該方法是就可以得到最新修改的資料,進而使用最新的資料來更新視圖元件的顯示。

從上面介紹不難看出,KVO程式設計的步驟如下:

1、為被監聽對象(通常是資料模型元件)注冊監聽器

2、重寫監聽器的-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context方法。

下方用一個簡單地類模拟視圖元件,下方代碼仔細看即可:

程式代碼:LYXItem.h

#import <Foundation/Foundation.h>

@interface LYXItem : NSObject
@property(nonatomic,copy) NSString *name;
@property(nonatomic,copy) NSString *price;
@end
           

程式代碼:LYXItemView.h

//  Created by lyx on 16/2/11.
//  Copyright (c) 2016年 李雲祥. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "LYXItem.h"

@interface LYXItemView : NSObject

//使用@property定義兩個屬性
@property(nonatomic,weak) LYXItem * item;
-(void)showItemInfo;

@end
           

程式代碼:LYXItemView.m

#import "LYXItemView.h"

@implementation LYXItemView
@synthesize item = _item;

-(void)showItemInfo
{
  
    NSLog(@"item名為:%@,價格為%@",self.item.name,self.item.price);
}
//自定義setItem方法
-(void)setItem:(LYXItem *)item
{
    self->_item = item;
    //為item添加監聽器,監聽item的name的屬性的變化
    [self.item addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    //為item添加監聽器,監聽item的price屬性的變化
    [self.item addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew context:nil];
    
}
//重寫該方法,當被監聽的資料模型發生改變時,就會回調監聽器的該方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"-------observeValueForKeyPath-----方法被調用");
    NSLog(@"被修改的keyPath為:%@",keyPath);
    NSLog(@"被修改的對象為:%@",object);
    NSLog(@"新被修改的屬性值是:%@",[change objectForKey:@"new"]);
    NSLog(@"被修改的上下文是:%@",context);
}
-(void)dealloc
{
    //删除監聽器
    [self.item removeObserver:self forKeyPath:@"name"];
    [self.item removeObserver:self forKeyPath:@"price"];
    
}
@end
           

程式代碼:main.m

#import <Foundation/Foundation.h>
#import "LYXItemView.h"
#import "LYXItem.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        //建立item對象
        LYXItem *item = [[LYXItem alloc]init];
        item.name = @"IOS";
        item.price = @"10";
        
        LYXItemView *itemView = [[LYXItemView alloc]init];
        //将itemView的item屬性設為item
        itemView.item = item;
        [itemView showItemInfo];
        //再次更改item的屬性,将會激發監聽器的方法
        item.name = @"安卓";
        item.price = @"100";
        
        
    }
    return 0;
}
           

輸出下方代碼:

2016-02-11 22:51:49.767 KVO操練[502:17531] item名為:IOS,價格為10
2016-02-11 22:51:49.768 KVO操練[502:17531] -------observeValueForKeyPath-----方法被調用
2016-02-11 22:51:49.768 KVO操練[502:17531] 被修改的keyPath為:name
2016-02-11 22:51:49.769 KVO操練[502:17531] 被修改的對象為:<LYXItem: 0x100304830>
2016-02-11 22:51:49.769 KVO操練[502:17531] 新被修改的屬性值是:安卓
2016-02-11 22:51:49.769 KVO操練[502:17531] 被修改的上下文是:(null)
2016-02-11 22:51:49.769 KVO操練[502:17531] -------observeValueForKeyPath-----方法被調用
2016-02-11 22:51:49.769 KVO操練[502:17531] 被修改的keyPath為:price
2016-02-11 22:51:49.769 KVO操練[502:17531] 被修改的對象為:<LYXItem: 0x100304830>
2016-02-11 22:51:49.770 KVO操練[502:17531] 新被修改的屬性值是:100
2016-02-11 22:51:49.770 KVO操練[502:17531] 被修改的上下文是:(null)
Program ended with exit code: 0           

可以看出,當使用KVO後,此時的程式當被監聽的資料模型元件LYXItem的屬性發生改變,監聽器的監聽方法就會被激發。要是在視圖控制器上,那麼這裡的UI就會進行改變。

KVO(鍵值監聽)詳解