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

2022-04-18  本文已影响0人  ShawnAlex

OC面试题目合集地址

问题1:什么是KVO

答案:

swizzling: 旋转


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

分析:

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

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

这个方法底层实现为


isa混写原理

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

KVO生效

KVO代码实现:

观察者例子

其中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方法代码:

关键代码

原理

// 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方法

上一篇 下一篇

猜你喜欢

热点阅读