手动 触发 kvo
过程如下:
- 重写
automaticallyNotifiesObserversForKey
返回 NO - 非可变容器属性 在 set 中 添加方法
willChangeValueForKey
,didChangeValueForKey
- 可变容器属性根据 kvc 创建 代理对象,并在里面调用方法
willChange:valuesAtIndexes:forKey:
,didChange:valuesAtIndexes:forKey:
- kvo 响应方法
observeValueForKeyPath:ofObject:change:context:.
具体过程
- 重新方法
automaticallyNotifiesObserversForKey
, 对需要手动控制 kvo 的 key 返回 NO
我们把 属性 name, number, arr, mutableArr 都设为手动触发 kvo
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
BOOL automatic = NO;
if ([key isEqualToString:@"name"]) {
automatic = NO;
}else if ([key isEqualToString:@"number"]){
automatic = NO;
}else if ([key isEqualToString:@"arr"]){
automatic = NO;
}else if ([key isEqualToString:@"mutableArr"]){
automatic = NO;
}else {
automatic = [super automaticallyNotifiesObserversForKey:key];
}
return automatic;
}
- 在属性 set 方法中手动触发 kvo,
willChangeValueForKey
,didChangeValueForKey
// 手动改管理 kvo 触发,有个明显的好处,就是,我们在不需要触发kvo时可以很容易的停止触发,如下
// 当 name 在设值 前和 设值后 是一样的比如都是 王小小,我们就可以不触发kvo
// 如果是 自动 kvo 就无法达到这样的控制效果。
- (void)setName:(NSString *)name{
if (![_name isEqualToString:name]) {
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}
}
// 设值 kvo 触发条件, 只有当 bunber > 10 的时候,才会触发 kvo。
- (void)setNumber:(int)number{
if (number>10) {
[self willChangeValueForKey:@"number"];
_number = number;
[self didChangeValueForKey:@"number"];
}
}
- (void)setArr:(NSArray<NSString *> *)arr{
[self willChangeValueForKey:@"arr"];
_arr = arr;
[self didChangeValueForKey:@"arr"];
}
- 在可变容器中触发 kvo, 结合 kvc 可变容器代理对象创建。
需要实现一对删除和插入的方法, 并在这些方法中调用方法willChange:valuesAtIndexes:forKey:
,didChange:valuesAtIndexes:forKey:
// 可变容器添加手动kvo,可变容器内部元素发生添加,移除,替换时触发kvo
// 插入单个 元素
- (void)insertObject:(NSString *)object inMutableArrAtIndex:(NSUInteger)index{
[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"mutableArr"];
[_mutableArr insertObject:object atIndex:index];
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"mutableArr"];
}
// 删除 单个元素
- (void)removeObjectFromMutableArrAtIndex:(NSUInteger)index{
[self willChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"mutableArr"];
[_mutableArr removeObjectAtIndex:index];
[self didChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"mutableArr"];
}
// 插入 多个元素
- (void)insertMutableArr:(NSArray *)array atIndexes:(NSIndexSet *)indexes{
[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"mutableArr"];
[_mutableArr insertObjects:array atIndexes:indexes];
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"mutableArr"];
}
// 删除多个元素
- (void)removeMutableArrAtIndexes:(NSIndexSet *)indexes{
[self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@"mutableArr"];
[_mutableArr removeObjectsAtIndexes:indexes];
[self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@"mutableArr"];
}
// 替换 单个
- (void)replaceObjectInMutableArrAtIndex:(NSUInteger)index withObject:(id)object{
[self willChange:NSKeyValueChangeReplacement valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"mutableArr"];
[_mutableArr replaceObjectAtIndex:index withObject:object];
[self didChange:NSKeyValueChangeReplacement valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"mutableArr"];
}
// 替换 多个
- (void)replaceMutableArrAtIndexes:(NSIndexSet *)indexes withMutableArr:(NSArray *)array{
[self willChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:@"mutableArr"];
[_mutableArr replaceObjectsAtIndexes:indexes withObjects:array];
[self didChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:@"mutableArr"];
}
外部调用
// 必须通过 kvc 拿到 属性
NSMutableArray *mutableArr = [obj mutableArrayValueForKey:@"mutableArr"];
// 添加一个元素
[mutableArr addObject:@"100"];
// 删除一个元素
[mutableArr removeObjectAtIndex:0];
// 添加多个元素
[mutableArr addObjectsFromArray:@[@"90",@"91"]];
// 删除多个元素
[mutableArr removeObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]];
// 替换一个元素
[mutableArr replaceObjectAtIndex:0 withObject:@"替换"];
// 替换多个元素
NSArray *objs = @[@"替换: 1",@"替换: 2"];
[mutableArr replaceObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, objs.count)] withObjects:objs];
- observeValueForKeyPath 响应
- change 参数:这是一个字典里面包含的key 有
kind: NSKeyValueChange,跟willChangeValueForKey
中的保持一致
indexes: String, 元素变更的位置或者 range
new: 设值的新值
old: 之前的旧值
- change 参数:这是一个字典里面包含的key 有