设为首页 加入收藏

TOP

【OC底层】KVO原理(一)
2019-08-31 00:22:09 】 浏览:67
Tags:底层 KVO 原理

KVO的原理是什么?底层是如何实现的?

KVO是Key-value observing的缩写。

KVO是Objective-C是使用观察者设计模式实现的。

Apple使用了isa混写(isa-swizzling)来实现KVO。

 

我们可以通过代码去探索一下。

创建自定义类:XGPerson

@interface XGPerson : NSObject

@property (nonatomic,assign) int age;

@property (nonatomic,copy) NSString* name;

@end

我们的思路就是看看对象添加KVO之前和之后有什么变化,是否有区别,代码如下:

@interface ViewController ()

@property (strong, nonatomic) XGPerson *person1;
@property (strong, nonatomic) XGPerson *person2;

@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person1 = [[XGPerson alloc]init];
    self.person2 = [[XGPerson alloc]init];
    self.person1.age = 1;
    self.person2.age = 10;

    // 添加监听之前,获取类对象,通过两种方式分别获取 p1 和 p2的类对象
    NSLog(@"before getClass--->> p1:%@  p2:%@",object_getClass(self.person1),object_getClass(self.person2));
    NSLog(@"before class--->> p1:%@  p2:%@",[self.person1 class],[self.person2 class]);
        
    // 添加KVO监听
    NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:option context:nil];

    // 添加监听之后,获取类对象
    NSLog(@"after getClass--->> p1:%@  p2:%@",object_getClass(self.person1),object_getClass(self.person2));
    NSLog(@"after class--->> p1:%@  p2:%@",[self.person1 class],[self.person2 class]);
}

输出:

2018-11-02 15:16:13.276167+0800 KVO原理[4083:170379] before getClass--->> p1:XGPerson  p2:XGPerson
2018-11-02 15:16:13.276271+0800 KVO原理[4083:170379] before class--->> p1:XGPerson  p2:XGPerson


2018-11-02 15:16:13.276712+0800 KVO原理[4083:170379] after getClass--->> p1:NSKVONotifying_XGPerson  p2:XGPerson
2018-11-02 15:16:13.276815+0800 KVO原理[4083:170379] after class--->> p1:XGPerson  p2:XGPerson

从上面可以看出,object_getClass 和 class 方式分别获取到的 类对象竟然不一样,在对象添加了KVO之后,使用object_getClass的方式获取到的对象和我们自定义的对象不一样,而是NSKVONotifying_XGPerson,可以怀疑 class 方法可能被篡改了.

最终发现NSKVONotifying_XGPerson是使用Runtime动态创建的一个类,是XGPerson的子类.

看完对象,接下来我们来看下属性,就是被我们添加了KVO的属性age,我们要触发KVO回调就是去给age设置个值,那它肯定就是调用setAge这个方法.

下面监听下这个方法在被添加了KVO之后有什么不一样.

    NSLog(@"person1添加KVO监听之前 - %p %p",
              [self.person1 methodForSelector:@selector(setAge:)],
              [self.person2 methodForSelector:@selector(setAge:)]);


    // 添加KVO监听
    NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:option context:nil];

    NSLog(@"person1添加KVO监听之后 - %p %p",
          [self.person1 methodForSelector:@selector(setAge:)],
          [self.person2 methodForSelector:@selector(setAge:)]);

输出:

2018-11-02 15:16:13.276402+0800 KVO原理[4083:170379] person1添加KVO监听之前 - 0x10277c3e0 0x10277c3e0

2018-11-02 15:16:17.031319+0800 KVO原理[4083:170379] person1添加KVO监听之后 - 0x102b21f8e 0x10277c3e0

看输出我们能发现,在监听之前两个对象的方法所指向的物理地址都是一样的,添加监听后,person1对象的setAge方法就变了,这就说明一个问题,这个方法的实现变了,我们再通过Xcode断点调试打印看下到底调用什么方法

断点后,在调试器中使用 po 打印对象

(lldb) po [self.person1 methodForSelector:@selector(setAge:)]

  (Foundation`_NSSetIntValueAndNotify)

 

(lldb) po [self.person2 methodForSelector:@selector(setAge:)]

  (KVO原理`-[XGPerson setAge:] at XGPerson.m:13)

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇iOS开发之OC与swift开发混编教程.. 下一篇给button增加下划线

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目