iOS KVO初探

ppsheep發表於2016-11-05

歡迎大家關注我的公眾號,我會定期分享一些我在專案中遇到問題的解決辦法和一些iOS實用的技巧,現階段主要是整理出一些基礎的知識記錄下來

iOS KVO初探

文章也會同步更新到我的部落格:
ppsheep.com

在Objc中有一種觀察者模式,即是Key Value Observing(KVO)。利用KVO可以很容易實現檢視元件和資料模型的分離。當資料模型的值改變時,會馬上觸發檢視元件,更新檢視元件。在Objc中要實現KVO,必須實現NSKeyValueObServing協議,所幸的是NSObject已經實現該協議,也就是說,幾乎所有的Objc物件都可以使用KVO。

在OC中,KVO的使用步驟一般是:

  • 被監聽者通過 addObserver:forKeyPath:options:context: 方法,新增監聽
  • 監聽者重寫 observeValueForKeyPath:ofObject:change:context: 方法,實現監聽
  • 被監聽者移除監聽

簡單的實現一下,首先,我需要一個監聽物件,所以建立一個Entity

//  Entity.h
#import 

@interface Entity : NSObject

@property (nonatomic, copy) NSString *name;

/**
 因為另外兩個屬性沒有暴露出來 我們只能通過方法來改變
 */
- (void)changeName1:(NSString *)name1;

- (void)changeName2:(NSString *)name2;

@end複製程式碼
//  Entity.m
@interface Entity()

@property (nonatomic, copy) NSString *name1;

@end

@implementation Entity
{
    @private
    NSString *_name2;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _name = @"name";
        _name1 = @"name1";
        _name2 = @"name2";
    }
    return self;
}

-(void)changeName1:(NSString *)name1{
    _name1 = name1;
}

-(void)changeName2:(NSString *)name2{
    _name2 = name2;
}

@end複製程式碼

這裡 我們建了三個屬性,name,name1,name2, 每個name屬性都是不一樣的定義,這也是我們日常coding中,最常見的三種屬性定義方式。

待會我們試著對這三個屬性都進行監聽,看一下效果如何?

被監聽者,已經建立完成了,我們現在需要一個監聽者

self.model  = [[Entity alloc] init];

    UIButton *btn1 = [[UIButton alloc] initWithFrame:CGRectMake(100, 100,200, 30)];
    btn1.tag = 100001;
    [btn1 addTarget:self action:@selector(changeName:) forControlEvents:UIControlEventTouchUpInside];
    [btn1 setTitle:@"改變name" forState:UIControlStateNormal];
    [self.model addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

    UIButton *btn2 = [[UIButton alloc] initWithFrame:CGRectMake(100, 200,200, 30)];
    btn2.tag = 100002;
    [btn2 addTarget:self action:@selector(changeName:) forControlEvents:UIControlEventTouchUpInside];
    [btn2 setTitle:@"改變值name1" forState:UIControlStateNormal];
     [self.model addObserver:self forKeyPath:@"name1" options:NSKeyValueObservingOptionNew context:nil];


    UIButton *btn3 = [[UIButton alloc] initWithFrame:CGRectMake(100, 300,200, 30)];
    btn3.tag = 100003;
    [btn3 addTarget:self action:@selector(changeName:) forControlEvents:UIControlEventTouchUpInside];
    [btn3 setTitle:@"改變值name2" forState:UIControlStateNormal];
    [self.model addObserver:self forKeyPath:@"name2" options:NSKeyValueObservingOptionNew context:nil];

    [self.view addSubview:btn1];
    [self.view addSubview:btn2];
    [self.view addSubview:btn3];複製程式碼

三個按鈕分別對應三個屬性,點選每個按鈕 觸發不同的KVO

改變model的屬性

- (void)changeName: (UIButton *)btn{
    switch (btn.tag) {
        case 100001:
            self.model.name = @"change";
            break;
        case 100002:
            [self.model changeName1:@"change"];
            break;
        case 100003:
            [self.model changeName2:@"changge"];
            break;
        default:
            break;
    }
}複製程式碼

當然一定不要忘記了,重寫observeValueForKeyPath方法:

//KVO監聽回撥
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    NSLog(@"%@", keyPath);
}複製程式碼

列印出當前的keypath

一定不要忘記移除監聽

-(void)dealloc{
    [self.model removeObserver:self forKeyPath:@"name"];
    [self.model removeObserver:self forKeyPath:@"name1"];
    [self.model removeObserver:self forKeyPath:@"name2"];
}複製程式碼

經過試驗 我們發現 只有點選第一個按鈕,即name屬性對應的按鈕,才會觸發監聽。

從這一點,我們可以發現,只有在.h中暴露出來的屬性,才能被KVO監聽到,.m中的屬性,是不能夠被監聽到。

一個簡單的KVO

demo放在:

github.com/yangqian111…

相關文章