美文网首页
iOS-底层原理- KVO的基本使用

iOS-底层原理- KVO的基本使用

作者: 如意神王 | 来源:发表于2021-12-12 14:15 被阅读0次

    1.KVO是做什么的

    1.KVO : (Key - Value - Observer) 键值观察者,KVO全称KeyValueObserving,是苹果提供的一套事件通知机制。允许对象监听另一个对象属性的改变,并在改变时接收到事件。由于KVO的实现机制,所以对属性才会发生作用,一般继承自NSObject的对象都默认支持KVO。

    \color{red}{2.KVO只能观察属性}

    2.基本使用步骤

    1.注册观察者
    ViewController观察Person类name属性的变化 context参数提高可读性+便于回调方法区分

    static void * PersonNameContext = &PersonNameContext;
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        self.m_person = [[Person alloc] init];
        self.m_person.name = @"LiLei";
        [self.m_person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:PersonNameContext];
    }
    

    2.实现回调方法
    ViewController里面书写iOS系统的KVO回调方法

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        if (context == PersonNameContext) {
            NSLog(@"keypath == %@", keyPath);
            NSLog(@"object == %@", object);
            NSLog(@"change == %@", change);
            NSLog(@"change[NSKeyValueChangeNewKey] == %@", change[NSKeyValueChangeNewKey]);
        }
    }
    

    3.修改被观察对象的属性值
    touchBegan方法里修改self.m_person.name的属性值

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        self.m_person.name = [NSString stringWithFormat:@"%@+", self.m_person.name];
    }
    

    系统回调方法对应输出

    2021-12-12 13:19:37.462142+0800 KVO_18_001_SBY[2215:100253] keypath == name
    2021-12-12 13:19:37.462285+0800 KVO_18_001_SBY[2215:100253] object == <Person: 0x600002cf8a20>
    2021-12-12 13:19:37.462460+0800 KVO_18_001_SBY[2215:100253] change == {
        kind = 1;
        new = "LiLei+";
    }
    2021-12-12 13:19:37.462562+0800 KVO_18_001_SBY[2215:100253] change[NSKeyValueChangeNewKey] == LiLei+
    

    4.移除观察者
    合适的位置移除观察者

    - (void)dealloc {
        [self.m_person removeObserver:self forKeyPath:@"name" context:PersonNameContext];
    }
    

    3.KVO使用小细节

    1.禁止观察某个属性值,系统默认是YES而且不用书写出来,如果返回NO的话name这个属性值就观察不了了,每个属性都有一个是否自动观察的方法

    // 默认是YES可以观察并且没有这行代码 name属性,返回NO的时候观察不了了
    + (BOOL)automaticallyNotifiesObserversOfName {
        return NO;
    }
    

    2.手动观察属性
    即使automaticallyNotifiesObserversOfName返回NO,关闭自动观察,手动打开下面的代码依然可以观察

    - (void)setName:(NSString *)name {
        [self willChangeValueForKey:@"name"];
        _name = name;
        [self didChangeValueForKey:@"name"];
    }
    

    3.观察数组,修改属性值的时候需要用[self.m_person mutableArrayValueForKey:@"dateArray"],不能self.m_person.dateArray

    self.m_person.dateArray = [NSMutableArray arrayWithCapacity:0];
    [self.m_person addObserver:self forKeyPath:@"dateArray" options:NSKeyValueObservingOptionNew context:PersonNameContext];
    
    [[self.m_person mutableArrayValueForKey:@"dateArray"] addObject:[NSString stringWithFormat:@"%lu", (unsigned long)count]];
    

    4.复合观察、路径观察,多个属性合并成一个属性进行观察

    [self.m_person addObserver:self forKeyPath:@"downloadProgress" options:NSKeyValueObservingOptionNew context:PersonNameContext];
    
    // 下载进度 -- writtenData/totalData,这两个值的观察变成一个值downloadProgress的观察
    + (NSSet <NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
        NSSet * keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
        if ([key isEqualToString:@"downloadProgress"]) {
            NSArray * affectingKeys = @[@"totalData", @"writtenData"];
            keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
        }
        return keyPaths;
    }
    
    // 复合路径的开关,默认是YES可以不书写,如果返回NO,不能观察downloadProgress属性
    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
        return YES;
    }
    
    self.m_person.writtenData += 10;
    self.m_person.totalData = 100;
    

    输出

    2021-12-12 14:14:33.304174+0800 KVO_18_001_SBY[1006:28002] keypath == downloadProgress
    2021-12-12 14:14:33.304275+0800 KVO_18_001_SBY[1006:28002] object == <Person: 0x6000028750e0>
    2021-12-12 14:14:33.304397+0800 KVO_18_001_SBY[1006:28002] change == {
        kind = 1;
        new = "0.100000";
    }
    2021-12-12 14:14:33.304475+0800 KVO_18_001_SBY[1006:28002] change[NSKeyValueChangeNewKey] == 0.100000
    2021-12-12 14:14:33.304549+0800 KVO_18_001_SBY[1006:28002] keypath == downloadProgress
    2021-12-12 14:14:33.304616+0800 KVO_18_001_SBY[1006:28002] object == <Person: 0x6000028750e0>
    2021-12-12 14:14:33.304700+0800 KVO_18_001_SBY[1006:28002] change == {
        kind = 1;
        new = "0.100000";
    }
    2021-12-12 14:14:33.304748+0800 KVO_18_001_SBY[1006:28002] change[NSKeyValueChangeNewKey] == 0.100000
    2021-12-12 14:14:33.304809+0800 KVO_18_001_SBY[1006:28002] keypath == downloadProgress
    2021-12-12 14:14:33.304856+0800 KVO_18_001_SBY[1006:28002] object == <Person: 0x6000028750e0>
    2021-12-12 14:14:33.304950+0800 KVO_18_001_SBY[1006:28002] change == {
        kind = 1;
        new = "0.100000";
    }
    2021-12-12 14:14:33.305008+0800 KVO_18_001_SBY[1006:28002] change[NSKeyValueChangeNewKey] == 0.100000
    

    相关文章

      网友评论

          本文标题:iOS-底层原理- KVO的基本使用

          本文链接:https://www.haomeiwen.com/subject/tcjrfrtx.html