使用Block实现KVO

在iOS开发中,我们可以通过KVO机制来监听某个对象的某个属性的变化。

用过KVO的同学都应该知道,KVO的回调是以代理的形式实现的:在给某个对象添加观察以后,需要在另外一个地方实现回调代理方法。这种设计给人感觉比较分散,因此突然想试试用Block来实现KVO,将添加观察的代码和回调处理的代码写在一起。在学习了ImplementKVO的实现以后,自己也写了一个:SJKVOController

使用Block实现KVO
使用Block来实现KVO

SJKVOController的用法

只需要引入NSObject+SJKVOController.h头文件就可以使用SJKVOController。
先看一下它的头文件:

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

@interface NSObject (SJKVOController)


//============== add observer ===============//
- (void)sj_addObserver:(NSObject *)observer forKeys:(NSArray <NSString *>*)keys withBlock:(SJKVOBlock)block;
- (void)sj_addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(SJKVOBlock)block;


//============= remove observer =============//
- (void)sj_removeObserver:(NSObject *)observer forKeys:(NSArray <NSString *>*)keys;
- (void)sj_removeObserver:(NSObject *)observer forKey:(NSString *)key;
- (void)sj_removeObserver:(NSObject *)observer;
- (void)sj_removeAllObservers;


//============= list observers ===============//
- (void)sj_listAllObservers;

@end

从上面的API可以看出,这个小轮子:

  1. 支持一次观察同一对象的多个属性。
  2. 可以一次只观察一个对象的一个属性。
  3. 可以移除对某个对象对多个属性的观察。
  4. 可以移除对某个对象对某个属性的观察。
  5. 可以移除某个观察自己的对象。
  6. 可以移除所有观察自己的对象。
  7. 打印出所有观察自己的对象的信息,包括对象本身,观察的属性,setter方法。

下面来结合Demo讲解一下如何使用这个小轮子:

在点击上面两个按钮中的任意一个,增加观察:

一次性添加:

- (IBAction)addObserversTogether:(UIButton *)sender {

    NSArray *keys = @[@"number",@"color"];

    [self.model sj_addObserver:self forKeys:keys withBlock:^(id observedObject, NSString *key, id oldValue, id newValue) {

        if ([key isEqualToString:@"number"]) {

            dispatch_async(dispatch_get_main_queue(), ^{
                self.numberLabel.text = [NSString stringWithFormat:@"%@",newValue];
            });

        }else if ([key isEqualToString:@"color"]){

            dispatch_async(dispatch_get_main_queue(), ^{
                self.numberLabel.backgroundColor = newValue;
            });
        }

    }];
}

分两次添加:

- (IBAction)addObserverSeparatedly:(UIButton *)sender {

    [self.model sj_addObserver:self forKey:@"number" withBlock:^(id observedObject, NSString *key, id oldValue, id newValue) {

        dispatch_async(dispatch_get_main_queue(), ^{
            self.numberLabel.text = [NSString stringWithFormat:@"%@",newValue];
        });

    }];

    [self.model sj_addObserver:self forKey:@"color" withBlock:^(id observedObject, NSString *key, id oldValue, id newValue) {

        dispatch_async(dispatch_get_main_queue(), ^{
            self.numberLabel.backgroundColor = newValue;
        });

    }];

}

添加以后,点击最下面的按钮来显示所有的观察信息:

- (IBAction)showAllObservingItems:(UIButton *)sender {

    [self.model sj_listAllObservers];
}

输出:

SJKVOController[80499:4242749] SJKVOLog:==================== Start Listing All Observers: ==================== 
SJKVOController[80499:4242749] SJKVOLog:observer item:{observer: <ViewController: 0x7fa1577054f0> | key: color | setter: setColor:}
SJKVOController[80499:4242749] SJKVOLog:observer item:{observer: <ViewController: 0x7fa1577054f0> | key: number | setter: setNumber:}

在这里我重写了description方法,打印出了每个观察的对象和key,以及setter方法。

现在点击更新按钮,则会更新model的number和color属性,从而触发KVO:

- (IBAction)updateNumber:(UIButton *)sender {

    //trigger KVO : number
    NSInteger newNumber = arc4random() % 100;
    self.model.number = [NSNumber numberWithInteger:newNumber];

    //trigger KVO : color
    NSArray *colors = @[[UIColor redColor],[UIColor yellowColor],[UIColor blueColor],[UIColor greenColor]];
    NSInteger colorIndex = arc4random() % 3;
    self.model.color = colors[colorIndex];
}

我们可以看到中间的Label上面显示的数字和背景色都在变化,成功实现了KVO:

使用Block实现KVO
同时观察颜色和数字的变化

现在我们移除观察,点击remove按钮

- (IBAction)removeAllObservingItems:(UIButton *)sender {
    [self.model sj_removeAllObservers];   
}

在移除了所有的观察者以后,则会打印出:

SJKVOController[80499:4242749] SJKVOLog:Removed all obserbing objects of object:<Model: 0x60000003b700>
1 2 3 4 
下一页