iOS面试题:KVC的赋值和取值过程是怎样的?KVO原理是什么?

1、KVC赋值

// 1.1 创建人
PTLPerson *p = [[PTLPerson alloc] init];
self.person = p;
// 1.2 创建狗
PTLDog *dog = [[PTLDog alloc] init];
// 1.3 将狗赋值给人
[p setValue:dog forKeyPath:@"dog"];
// 1.4 通过KVC给dog的weight属性赋值 
赋值时会自动找到人拥有的dog的weight属性
[p setValue:@10.0 forKeyPath:@"dog.weight"];
NSLog(@"books = %@", [p valueForKeyPath:@"dog.weight"]);
[dog print];

2、 KVC取值

NSMutableArray *tempM = [NSMutableArray array];
// 2.1 kvc取出出数组books中price的值
for (PTLBook *book in [p valueForKeyPath:@"books"]) {
    [tempM addObject:[book valueForKeyPath:@"price"]];
}
NSLog(@"%@", tempM);
// 2.2 kvc取出数组中price的最大值
NSLog(@"Max = %@", [[p valueForKeyPath:@"books"] valueForKeyPath:@"@max.price"]);

3、 KVO原理

  • KVO 是 Objective-C 对观察者设计模式的一种实现,另外一种是:通知机制(notification)
    KVO提供一种机制,指定一个被观察对象(例如A类),当对象某个属性(例如A中的字符串name)发生更改时,对象会获得通知,并作出相应处理
    在MVC设计架构下的项目,KVO机制很适合实现mode模型和controller之间的通讯。
    例如:代码中,在模型类A创建属性数据,在控制器中创建观察者,一旦属性数据发生改变就收到观察者收到通知,通过KVO再在控制器使用回调方法处理实现视图B的更新;(本文中的应用就是这样的例子.)
  • KVO在Apple中的API文档如下:
    Automatic key-value observing is implemented using a technique called isa-swizzling… When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class …
    KVO 的实现依赖于 Objective-C 强大的 Runtime【 ,从以上Apple 的文档可以看出苹果对于KVO机制的实现是一笔带过,而具体的细节没有过多的描述,但是我们可以通过Runtime的所提供的方法去探索关于KVO机制的底层实现原理.

  • 当观察某对象 A 时,KVO 机制动态创建一个对象A当前类的子类,并为这个新的子类重写了被观察属性 keyPath 的 setter 方法。setter 方法随后负责通知观察对象属性的改变状况。

  • Apple 使用了 isa 混写(isa-swizzling)来实现 KVO 。当观察对象A时,KVO机制动态创建一个新的名为:NSKVONotifying_A 的新类,该类继承自对象A的本类,且 KVO 为 NSKVONotifying_A 重写观察属性的 setter 方法,setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象属性值的更改情况。

  • NSKVONotifying_A类剖析:在这个过程,被观察对象的 isa 指针从指向原来的A类,被KVO机制修改为指向系统新创建的子类 NSKVONotifying_A类,来实现当前类属性值改变的监听;
    所以当我们从应用层面上看来,完全没有意识到有新的类出现,这是系统“隐瞒”了对KVO的底层实现过程,让我们误以为还是原来的类。但是此时如果我们创建一个新的名为“NSKVONotifying_A”的类(),就会发现系统运行到注册KVO的那段代码时程序就崩溃,因为系统在注册监听的时候动态创建了名为NSKVONotifying_A的中间类,并指向这个中间类了。
    因而在该对象上对 setter 的调用就会调用已重写的 setter,从而激活键值通知机制。

  • 子类setter方法剖析:KVO的键值观察通知依赖于 NSObject 的两个方法:willChangeValueForKey:和 didChangevlueForKey:,在存取数值的前后分别调用2个方法:
    被观察属性发生改变之前,willChangeValueForKey:被调用,通知系统该 keyPath 的属性值即将变更;当改变发生后, didChangeValueForKey: 被调用,通知系统该 keyPath 的属性值已经变更;
    之后observeValueForKey:ofObject:change:context: 也会被调用。且重写观察属性的setter 方法这种继承方式的注入是在运行时而不是编译时实现的。
    KVO为子类的观察者属性重写调用存取方法的工作原理在代码中相当于:
-(void)setName:(NSString *)newName 
{ 
[self willChangeValueForKey:@"name"]; //KVO在调用存取方法之前总调用 
[super setValue:newName forKey:@"name"]; //调用父类的存取方法 
[self didChangeValueForKey:@"name"]; //KVO在调用存取方法之后总调用 
} 

更多:iOS面试题合集

你可能感兴趣的:(iOS面试题:KVC的赋值和取值过程是怎样的?KVO原理是什么?)