面试题答案
一键面试KVC 与 KVO 在运行时的交互分析
- KVC(Key - Value Coding)原理:
- KVC是一种通过键来访问对象属性的机制。当使用
valueForKey:
方法时,运行时系统会按照特定的搜索模式查找对应的属性。首先会查找对象的get<Key>
、is<Key>
方法,如果不存在则查找_<key>
、<key>
实例变量。如果都未找到,会调用accessInstanceVariablesDirectly
方法,若该方法返回YES
,则继续搜索实例变量。若最终都未找到,会调用valueForUndefinedKey:
方法,默认实现会抛出异常。 - 对于
setValue:forKey:
方法,首先会查找set<Key>:
方法,如果不存在则同样遵循类似的查找实例变量的规则,最后若都未找到会调用setValue:forUndefinedKey:
方法,默认实现也会抛出异常。
- KVC是一种通过键来访问对象属性的机制。当使用
- KVO(Key - Value Observing)原理:
- KVO基于观察者模式。当一个对象(被观察对象)的属性值发生变化时,会通知所有注册的观察者对象。实现机制是通过运行时动态创建被观察对象的子类,并重写被观察属性的
setter
方法。在setter
方法中,会调用willChangeValueForKey:
和didChangeValueForKey:
方法,这两个方法会通知观察者属性值即将改变和已经改变。
- KVO基于观察者模式。当一个对象(被观察对象)的属性值发生变化时,会通知所有注册的观察者对象。实现机制是通过运行时动态创建被观察对象的子类,并重写被观察属性的
- 交互分析:
- KVO依赖KVC来获取和设置属性值。当使用KVO观察一个属性时,KVO通过KVC机制来访问属性的值。例如,当观察对象的某个属性变化时,KVO在通知观察者前,需要通过KVC获取属性的新值。同时,KVO通知观察者时传递的信息,也是基于KVC对属性的访问和变化感知。
复杂数据模型中利用 KVC 和 KVO 特性优化代码
- 提高性能:
- 减少不必要的观察:在复杂数据模型中,避免对所有属性都设置KVO观察。只对关键业务相关且会频繁变化的属性设置观察。例如,在一个电商应用中,商品的库存属性频繁变化且与业务紧密相关,而商品的描述属性很少变化,可以只对库存属性设置KVO观察。
- 批量处理变化:利用KVC的集合操作特性,当多个相关属性变化时,通过KVC的集合操作一次性处理,而不是每次属性变化都触发KVO通知。比如,有一个包含多个商品价格的数组,可以使用KVC的
@sum
操作一次性计算总价,而不是对每个商品价格设置KVO观察并分别计算总价。
- 提高可维护性:
- 分层管理数据模型:将复杂数据模型分层,使用KVC的嵌套键路径来访问深层属性。例如,有一个包含用户信息的复杂模型,用户有地址信息,地址又有城市信息。可以通过
user.valueForKeyPath:@"address.city"
来访问城市信息,使代码结构更清晰。 - 使用KVO进行解耦:在不同模块之间,通过KVO进行通信,减少模块间的直接依赖。例如,一个模块负责数据的获取和更新,另一个模块负责UI展示。数据模块通过KVO通知UI模块数据的变化,UI模块根据通知更新界面,这样两个模块之间的耦合度降低,代码更易维护。
- 分层管理数据模型:将复杂数据模型分层,使用KVC的嵌套键路径来访问深层属性。例如,有一个包含用户信息的复杂模型,用户有地址信息,地址又有城市信息。可以通过
举例说明
假设我们有一个复杂数据模型,一个Order
类包含多个Product
对象,Product
类有price
和quantity
属性,Order
类需要计算订单总价。
- 代码实现:
// Product.h @interface Product : NSObject @property (nonatomic, assign) CGFloat price; @property (nonatomic, assign) NSInteger quantity; @end // Product.m @implementation Product @end // Order.h @interface Order : NSObject @property (nonatomic, strong) NSMutableArray<Product *> *products; @property (nonatomic, assign) CGFloat totalPrice; - (void)updateTotalPrice; @end // Order.m @implementation Order - (void)updateTotalPrice { NSArray *prices = [self.products valueForKeyPath:@"price"]; NSArray *quantities = [self.products valueForKeyPath:@"quantity"]; NSNumber *sumOfPrices = [prices valueForKeyPath:@"@sum.self"]; NSNumber *sumOfQuantities = [quantities valueForKeyPath:@"@sum.self"]; self.totalPrice = [sumOfPrices floatValue] * [sumOfQuantities floatValue]; } @end
- 在这个例子中,
Order
类通过KVC的集合操作计算订单总价,提高了性能。同时,若Product
的price
或quantity
属性变化,可以通过在Product
类对这些属性设置KVO观察,在Order
类的观察方法中调用updateTotalPrice
方法来更新总价,实现了不同模块(Product
和Order
)之间的解耦,提高了可维护性。
- 在这个例子中,