MST

星途 面试题库

面试题:Objective-C中KVO在复杂数据结构下的应用

假设你有一个包含多层嵌套的自定义对象,例如一个`Person`对象,`Person`对象包含一个`Address`对象,`Address`对象又包含`City`等属性。如何通过KVO来监听`City`属性的变化?并且如果在对象关系动态变化(如`Address`对象被替换)的情况下,如何保证KVO的正常工作?
31.1万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试
  1. 基本KVO监听City属性变化
    • 首先,确保相关类继承自NSObject(在Objective - C中,KVO基于NSObject的键值观察机制)。
    • 在持有Person对象的类中,注册KVO监听:
    - (void)viewDidLoad {
        [super viewDidLoad];
        Person *person = [[Person alloc] init];
        [person addObserver:self forKeyPath:@"address.city" options:NSKeyValueObservingOptionNew context:nil];
    }
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        if ([keyPath isEqualToString:@"address.city"]) {
            NSString *newCity = change[NSKeyValueChangeNewKey];
            NSLog(@"City has changed to: %@", newCity);
        }
    }
    - (void)dealloc {
        [self.person removeObserver:self forKeyPath:@"address.city"];
    }
    
    • 在Swift中:
    class ViewController: UIViewController {
        var person: Person!
        override func viewDidLoad() {
            super.viewDidLoad()
            person = Person()
            person.addObserver(self, forKeyPath: "address.city", options:.new, context: nil)
        }
        override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
            if keyPath == "address.city" {
                if let newCity = change?[.newKey] as? String {
                    print("City has changed to: \(newCity)")
                }
            }
        }
        deinit {
            person.removeObserver(self, forKeyPath: "address.city")
        }
    }
    
  2. 处理对象关系动态变化(如Address对象被替换)
    • Objective - C
      • Address对象可能被替换时,除了监听address.city,还需要监听address本身的变化。
      • 注册对address的监听:
      - (void)viewDidLoad {
          [super viewDidLoad];
          Person *person = [[Person alloc] init];
          [person addObserver:self forKeyPath:@"address.city" options:NSKeyValueObservingOptionNew context:nil];
          [person addObserver:self forKeyPath:@"address" options:NSKeyValueObservingOptionNew context:nil];
      }
      - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
          if ([keyPath isEqualToString:@"address"]) {
              // 移除旧的`address.city`监听
              Address *oldAddress = change[NSKeyValueChangeOldKey];
              if (oldAddress) {
                  [oldAddress removeObserver:self forKeyPath:@"city"];
              }
              // 添加新的`address.city`监听
              Address *newAddress = change[NSKeyValueChangeNewKey];
              if (newAddress) {
                  [newAddress addObserver:self forKeyPath:@"city" options:NSKeyValueObservingOptionNew context:nil];
              }
          } else if ([keyPath isEqualToString:@"address.city"]) {
              NSString *newCity = change[NSKeyValueChangeNewKey];
              NSLog(@"City has changed to: %@", newCity);
          }
      }
      - (void)dealloc {
          [self.person removeObserver:self forKeyPath:@"address.city"];
          [self.person removeObserver:self forKeyPath:@"address"];
      }
      
    • Swift
      class ViewController: UIViewController {
          var person: Person!
          override func viewDidLoad() {
              super.viewDidLoad()
              person = Person()
              person.addObserver(self, forKeyPath: "address.city", options:.new, context: nil)
              person.addObserver(self, forKeyPath: "address", options:.new, context: nil)
          }
          override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
              if keyPath == "address" {
                  if let oldAddress = change?[.oldKey] as? Address {
                      oldAddress.removeObserver(self, forKeyPath: "city")
                  }
                  if let newAddress = change?[.newKey] as? Address {
                      newAddress.addObserver(self, forKeyPath: "city", options:.new, context: nil)
                  }
              } else if keyPath == "address.city" {
                  if let newCity = change?[.newKey] as? String {
                      print("City has changed to: \(newCity)")
                  }
              }
          }
          deinit {
              person.removeObserver(self, forKeyPath: "address.city")
              person.removeObserver(self, forKeyPath: "address")
          }
      }
      

这样在City属性变化以及Address对象被替换时,都能通过KVO正常监听并处理变化。