面试题答案
一键面试性能瓶颈分析
- 频繁触发属性观察者:每次属性值变化都触发属性观察者,若处理逻辑复杂,会导致频繁的计算开销。例如在一个循环中不断改变属性值,每次改变都执行复杂的属性观察者逻辑,会极大消耗性能。
- 键值编码的动态性开销:键值编码通过字符串来访问属性,相比直接访问属性,动态查找和反射机制会带来额外的性能开销。比如在大规模数据集合中通过键值编码频繁获取或设置属性值。
- 通知机制的额外开销:属性观察者和键值编码常与通知机制配合,过多的通知发送和接收处理也会造成性能负担。例如大量对象都在监听同一类型通知,每次通知广播都会唤醒众多接收者进行处理。
优化策略
- 减少不必要的触发:在属性观察者中添加条件判断,仅在真正需要处理时执行逻辑。例如:
class SomeClass {
var someProperty: Int = 0 {
didSet {
if someProperty != oldValue {
// 执行处理逻辑
}
}
}
}
- 缓存键值编码结果:对于频繁使用键值编码访问的属性,缓存其结果。例如:
class CacheKVC {
private var cachedValue: Any?
var someKey = "someProperty"
var targetObject: AnyObject?
func getValue() -> Any? {
if let cached = cachedValue {
return cached
}
if let target = targetObject, let value = target.value(forKey: someKey) {
cachedValue = value
return value
}
return nil
}
}
- 优化通知机制:采用更细粒度的通知策略,只对关心的部分发送通知,并且合理管理通知的注册和注销。例如使用
Notification.Name
的特定子名称来区分不同类型通知,避免大量无关通知的处理。
属性观察者与键值编码结合示例
假设我们有一个视图模型ViewModel
和一个视图View
,需要实现数据绑定。
class ViewModel: NSObject {
dynamic var data: String = "" {
didSet {
self.observeValue(forKeyPath: "data", of: self, change: nil, context: nil)
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "data" {
// 通知视图数据变化
NotificationCenter.default.post(name: Notification.Name("dataChanged"), object: self)
}
}
}
class View: NSObject {
let viewModel: ViewModel
init(viewModel: ViewModel) {
self.viewModel = viewModel
super.init()
viewModel.addObserver(self, forKeyPath: "data", options: [.new], context: nil)
NotificationCenter.default.addObserver(self, selector: #selector(updateUI), name: Notification.Name("dataChanged"), object: viewModel)
}
@objc func updateUI() {
// 根据ViewModel中的data更新UI
if let data = viewModel.value(forKey: "data") as? String {
print("Updating UI with data: \(data)")
}
}
deinit {
viewModel.removeObserver(self, forKeyPath: "data")
NotificationCenter.default.removeObserver(self, name: Notification.Name("dataChanged"), object: viewModel)
}
}
在上述代码中,ViewModel
通过属性观察者在data
属性变化时通知视图,View
通过键值编码获取ViewModel
中的数据并更新UI,同时合理管理了通知和观察者的注册与注销,实现了高效的数据绑定和响应式编程且具备扩展性。