底层二:KVO、KVC、NSNotification

600 阅读2分钟

KVO

  • 通过打印被监听实例对象isa指针,可以发现,被监听实例对象的isa不再指向类对象,而是指向NSKVONotifying_MJPerson
  • NSKVONotifying_MJPerson是使用Runtime动态创建的一个类,是MJPerson的子类

NSKVONotifying_MJPerson 伪代码

  • 通过[self.person1 methodForSelector:@selector(setAge:)]获取方法实现的地址
  • 通过LLDB 的命令p (IMP)0x1069189e4 获取方法的名称

未命名.png

#import "MJPerson.h"

@interface NSKVONotifying_MJPerson : MJPerson

@end

#import "NSKVONotifying_MJPerson.h"

@implementation NSKVONotifying_MJPerson

- (void)setAge:(int)age
{
    _NSSetIntValueAndNotify();
}

// 伪代码
void _NSSetIntValueAndNotify()
{
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

- (void)didChangeValueForKey:(NSString *)key
{
    // 通知监听器,某某属性值发生了改变
    [oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}

@end

NSKVONotifying_MJPerson 类对象指向自己的元类对象

未使用KVO监听的对象

kvo1.png

使用KVO监听的对象

kvo2.png

总结:使用了KVO,会产生一个MJPerson的子类(NSKVONotifying_MJPerson),会改变MJPersonisa指向新产生的子类,同时在新的子类里面会重写setAge:方法,以及新增三个方法

获取方法数组

- (void)printMethodNamesOfClass:(Class)cls
{
    unsigned int count;
    // 获得方法数组
    Method *methodList = class_copyMethodList(cls, &count);
    
    // 存储方法名
    NSMutableString *methodNames = [NSMutableString string];
    
    // 遍历所有的方法
    for (int i = 0; i < count; i++) {
        // 获得方法
        Method method = methodList[i];
        // 获得方法名
        NSString *methodName = NSStringFromSelector(method_getName(method));
        // 拼接方法名
        [methodNames appendString:methodName];
        [methodNames appendString:@", "];
    }
    
    // 释放
    free(methodList);
    
    // 打印方法名
    NSLog(@"%@ %@", cls, methodNames);
}

可以看到 NSKVONotifying_MJPerson 内部的方法名

NSKVONotifying_MJPerson类型实现
setAge:重写实现Foundation_NSSetIntValueAndNotify方法
class新增屏蔽内部实现,隐藏了NSKVONotifying_MJPerson类对象
dealloc新增清理现场
_isKVOA新增

_NSSetIntValueAndNotify内部实现

1. [self willChangeValueForKey:@"age"]
2. 原来的setter实现
3. [self didChangeValueForKey:@"age"]

didChangeValueForKey:内部会调用observer的observeValueForKeyPath:ofObject:change:context:方法

KVO的手动实现,手动调用 willChangeValueForKeydidChangeValueForKey 两个方法

_NSSet*ValueAndNotify 包括很多的基本数据类型 kvo3.png

KVC

使用KVC设置属性时候,会触发KVO,即使没有set方法,也会触发KVO,因为KVC底层调用了- (void)willChangeValueForKey:(NSString *)key- (void)didChangeValueForKey:(NSString *)key 两个方法

kvc1.png 举例: 查找顺序(属性名为age) _age _isAge age isAge

kvc2.png

NSNotification

  • 是使用观察者模式来实现的用于跨层传递消息的机制
  • 传递方式是一对多

通知和代理的区别:

  • 代理是代理模式实现的,通知是观察者模式实现的
  • 代理是一对一的,通知是一对多的