美文网首页
IOS面试题(类相关) --- KVO

IOS面试题(类相关) --- KVO

作者: ShawnAlex | 来源:发表于2022-04-18 21:43 被阅读0次

    OC面试题目合集地址

    问题1:什么是KVO

    答案:

    • KVO 是key-value observing的缩写
    • KVO 是OC对观察者模式又一实现
    • 苹果用isa混写(isa-swizzling)方式来实现KVO

    swizzling: 旋转


    问题2: isa混写在KVO中是怎么实现的?

    分析:

    当我们注册KVO时候, 会调用这个方法

    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath 
    options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
    

    这个方法底层实现为


    isa混写原理
    • 当我们创建类Test调用addObserver :(NSObject *)observer forKeyPath:这个方法的时候, 系统会为我们动态创建一个NSKVONotifying_Test的类, 并将test中的isa指针指向NSKVONotifying_Test

    • NSKVONotifying_TestTest子类, 目的是重写Test中的setter方法, 来达到实现改变观察者值的目的

    上面其实就是KVO的机制和原理

    KVO生效
    • 使用setter方法改变值KVO才会生效
    • 使用setValue:forKey:改变值KVO才会生效
    • 成员变量直接修改需手动添加KVO才会生效

    KVO代码实现:

    • 创建2个类SRObject, SRObserver
    观察者例子

    其中SRObject.h

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface SRObject : NSObject
    
    // 设置变量a, 观察变量a的变化
    @property (nonatomic, assign) NSInteger a;
    
    @end
    

    SRObject.m

    #import "SRObject.h"
    
    @implementation SRObject
    
    // 初始化方法
    - (instancetype)init {
        
        self = [super init];
        
        if (self) {
            _a = 0;
        }
        
        return self;
    }
    
    
    @end
    

    AppDelegate

    @implementation AppDelegate
    
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        
        // 创建类
        SRObject *obj = [[SRObject alloc] init];
        // 创建观察者
        SRObserver *observer = [[SRObserver alloc] init];
        
        // 调用kvo监听方法, 对a进行监听
        // forKeyPath 要与观察变量名字一致
        [obj addObserver:observer forKeyPath:@"a" options: NSKeyValueObservingOptionNew context:nil];
       
        // 对a赋值 
        obj.a = 666;
        
        return YES;
    }
    
    

    SRObserver.m

    #import "SRObserver.h"
    #import "SRObject.h"
    
    
    @implementation SRObserver
    
    // KVO回调方法
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    
        // 判断是否是观察的类, 观察的值
        if ([object isKindOfClass:[SRObject class]] && [keyPath isEqualToString:@"a"]) {
            
            // 获取值打印结果
            NSNumber *testNum = [change valueForKey:NSKeyValueChangeNewKey];
            NSLog(@"SRObserver打印结果: %@", testNum);
        }
        
    }
    
    @end
    
    创建子类
    我们可以先打2个断点, 然后po读一下当前类名
    可以看到当被观察者监听时会创建一个NSKVONotifying的子类

    运行结果:

    运行结果

    可看到有 SRObserver打印结果: 666

    KVO重写Set方法代码:

    关键代码

    • - (void)willChangeValueForKey:(NSString *)key;
    • - (void)didChangeValueForKey:(NSString *)key;

    原理

    // NSKVONotifying_A的setter实现
    
    - (void)setValue:(id)obj {
        
        [self willChangeValueForKey:@"a"];
        // 子类NSKVONotifying_A调用父类实现, 即原类实现
        [super setValue:obj]
        [self didChangeValueForKey:@"a"];
    }
    
    

    问题3: 成员变量赋值KVO是否生效

    上面例子中的SRObject中.h, .m 和AppDelegate我们稍微变化一下

    SRObject内部添加方法改变成员变量

    - (void)increase {
        
        _a += 857;
    }
    

    AppDelegate调用一下

    @implementation AppDelegate
    
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        
        SRObject *obj = [[SRObject alloc] init];
        SRObserver *observer = [[SRObserver alloc] init];
        
        // 调用kvo监听方法
        [obj addObserver:observer forKeyPath:@"a" options: NSKeyValueObservingOptionNew context:nil];
        
        //obj.a = 666;
        [obj increase];
       
        return YES;
    }
    
    

    运行一下, 可以发现并无监听到, 不能监听到

    但是我们可以通过手动KVO方式, 触发生效, 修改increase方法

    - (void)increase {
        // 模拟系统写set 方法
        [self willChangeValueForKey:@"a"];
        _a += 857;
        [self didChangeValueForKey:@"a"];
    }
    
    
    手动KVO方式

    didChangeValueForKey 方法之后会触发KVO回调


    问题4: KVC设置成员变量赋值, KVO是否能生效

    上面例子中的AppDelegate我们稍微变化一下

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        
        SRObject *obj = [[SRObject alloc] init];
        SRObserver *observer = [[SRObserver alloc] init];
        
        // 调用kvo监听方法
        [obj addObserver:observer forKeyPath:@"a" options: NSKeyValueObservingOptionNew context:nil];
        
        // obj.a = 666;
        // KVC 方法
        [obj setValue:@"12345" forKey:@"a"];
        return YES;
    }
    
    
    运行结果

    KVC中setValue: forKey: 其实调用对象的set方法 与 .a = 666 是一致的

    我们可以在SRObject中验证一下KVC是否走了对象set方法 如下

    KVC调用set方法验证

    重写set方法, 可看到KVC也是调用对象set方法

    相关文章

      网友评论

          本文标题:IOS面试题(类相关) --- KVO

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