Demo地址: https://github.com/hanhuitao/KVO-NSMutableArray.git
iOS 中 KVO (key-value-observing) 的原理,简单来说就是重写了被观察属性的 set 方法,一般情况下只有通过调用 set 方法对值进行改变才会触发 KVO,直接访问实例变量修改值是不会触发 KVO 的。
先上代码
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic, strong)NSMutableArray * array;
@end
@implementation ViewController
-(NSMutableArray*)array
{
if (!_array) {
_array=[NSMutableArray new];
}
return _array;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.array addObjectsFromArray:@[@"黑色",@"白色"]];
NSLog(@"初始化地址=%p",self.array);
[self addObserver:self forKeyPath:@"array" options:NSKeyValueObservingOptionNew context:nil];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// [self.array addObject:@"苍白色"];
// [[self mutableArrayValueForKey:@"array"]addObject:@"苍白色"];
NSLog(@"改变数据源之后=%p",self.array);
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
NSLog(@"keyPath=%@,object=%@,change=%@,context=%@",keyPath,object,change,context);
}
@end
我们注意看touchesBegan里面的两行代码
1.使用[self.array addObject:@"苍白色"]添加对象
可以看出:是没有触发kvo的代理方法的,array的前后内存地址不变
2.使用 [[self mutableArrayValueForKey:@"array"]addObject:@"苍白色"]添加对象
可以看出:触发了kvo的代理方法的,并且array的前后内存地址发生了变化
为什么会这样:
NSMutableArray在调用它的 addObject、removeObject 系列方法时,并不会触发它自己的 set 方法。所以,对一个可变数组进行观察,在它加减元素时不会收到期望的消息。
因为KVO的本质是系统监测到或常量改变时,会添加上- (void)willChangeValueForKey:(NSString *)key和- (void)didChangeValueForKey:(NSString *)key方法来发送通知,所以一种解决方法是手动调用者两个方法,但是并不推荐,你永远无法像系统一样真正知道这个元素什么时候被改变。另一种便是利用使用mutableArrayValueForKey:了。
那么为什么mutableArrayValueForKey:这个方法可以监听dataSource内部变化呢?
mutableArrayValueForKey:默认采用的是搜索模式,在NSMutableArray搜索匹配insertObject:atIndex:和removeObjectAtIndex:等能引起容器内部Object发生改变的方法,如果发现至少一个插入或者删除方法,就会发送发送消息给原始接受者。
网友评论