KVC
setValue:forKey
- 先去查找setValue 、_setValue 方法
- 如果找不到步骤1 ,会去访问*accessInstanceVariablesDirectly 是否返回为true
- 如果是ture 就去找实例变量 _value 、_isValue 、value、isValue
- 如果找不到会调用setValue:(id)value forUndefinedKey:(NSString * *)key 抛出错误
*valueForKey:
- 先去找 getValue 、value 、 isValue、_value 方法
- 如果找不到步骤1的方法 会去访问accessInstanceVariablesDirectly 是否为true
- 如果步骤2 为true 就去找实例变量 _value、_isValue 、value 、isValue
- 如果找不到会调用 valueForUndefinedKey:
mutableArrayValueForKey
使用这个方法会改变原来数组 内存地址空间,说明创建了新对象
KVO
addObserver:forKeyPath:options:context:
- 先分析context 当对个对象被监听,然后都集中走到 回调方法里边,但是此时不知道这个是监听那个对象发过来的,这个时候给一个上下文,判断识别一下是从那个对象过来的
- observer 是否需要移除,iOS9 以后不需要手动移除观察者,会在被观察者销毁的时候自动移除,但是有一个案例 就是 被观察者如果是一个单例,那么此时如果观察者已经被销魂,那么会发生野指针错误
- 手动触发KVO kvo 目前都是自动触发的但是可以通过设置 automaticallyNotifiesObserversForKey 为NO 来关闭自动触发能力,然后需要重写setter方法,赋值前后调用willChangeValueForKey: 和 didChangeValueForKey:方法
- 依赖多个keys 当一个值的变化依赖多个 keys 的时候 ,比如 hashValue 的改变依赖了,name、uid等,那么我们就需要 keyPathsForValuesAffectingValueForKey 来实现
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
NSSet * set = [super keyPathsForValuesAffectingValueForKey: key];
if([key isEqualToString:@"name"]){
NSArray * arr = @[@"uid"];
set = [set setByAddingObjectsFromArray:arr];
}
return set;
}
- 数组类型处理 如果监听的属性是一个数组那么复制时需要通过 mutableArrayValueForKey 来 进行赋值
原理分析
kvo官方文档指出主要是isa 指向变了,那我就有一个问题,他现在指向了什么呢,分析一下isa 的指向
-(void)printClasses:(Class)class{
int count = objc_getClassList(NULL, 0);
Class * clasess = (Class *)malloc(sizeof(Class) * count);
objc_getClassList(clasess, count);
NSMutableArray * array = [NSMutableArray array];
for (int i = 0; i < count; i++) {
if(class == class_getSuperclass(clasess[i])){
NSLog(@"%@",clasess[i]);
[array addObject:clasess[i]];
}
}
free(clasess);
NSLog(@"%@----%d",array,count);
}
通过这个方法我们能查出来isa 会指向一个NSKVONotifying_xx 的类,那么需要分析一下生成这个类以后做了些什么事,
-(void)printMethodesInClass:(Class)class{
unsigned int count = 0;
Method * methodList = class_copyMethodList(class, &count);
for (int i =0; i < count; i++) {
Method method = methodList[i];
SEL sel = method_getName(method);
IMP imp = method_getImplementation(method);
NSLog(@"%@-----%p",NSStringFromSelector(sel),imp);
}
free(methodList);
}
- _isKVOA 方法是 kvo 的一个标识
- dealloc 判断是不是在释放,(干嘛用)用来重新指向isa
- class 伪装,我们外边打印class 返回的可不是 NSKVONotifying_xx
- 要变化属性的setter方法 这个setter 做了什么事,我们断点看一下 ,如何断点 通过LLDB 的 watchpoint set variable + 变量 来断点