MST

星途 面试题库

面试题:Swift键值编码的原理及复杂使用场景

详细说明Swift键值编码(KVC)的底层原理,以及如何使用KVC在一个复杂的自定义对象层级结构中,通过路径访问到深层嵌套对象的属性,并对其进行修改,同时考虑到可能出现的异常情况及处理方式。
26.8万 热度难度
编程语言Swift

知识考点

AI 面试

面试题答案

一键面试

Swift键值编码(KVC)底层原理

  1. 基本概念:KVC是一种通过键来访问对象属性的机制,它提供了一种间接访问对象属性的方法,而不仅仅依赖于直接的属性访问语法。
  2. 查找路径
    • 当通过KVC访问属性时,对象首先会在自身的属性列表中查找对应的键。如果找到了匹配的属性,就直接返回该属性的值。
    • 如果在自身属性列表中未找到,对象会检查是否有accessInstanceVariablesDirectly方法返回true(默认是true)。若为true,则会按照_<key>_is<Key><key>is<Key>的顺序查找实例变量。
    • 如果仍然未找到,对象会调用valueForUndefinedKey(_:)方法,默认实现会抛出NSUndefinedKeyException异常。
  3. 设值过程
    • 当通过KVC设置属性值时,对象会首先查找set<Key>:形式的方法。如果找到,就调用该方法来设置值。
    • 如果没有找到set<Key>:方法,对象会检查accessInstanceVariablesDirectly是否为true。若为true,则会按照与取值类似的顺序查找实例变量并设置值。
    • 如果都未成功,对象会调用setValue:forUndefinedKey:方法,默认实现会抛出NSUndefinedKeyException异常。

在复杂自定义对象层级结构中通过路径访问深层嵌套对象属性并修改

  1. 示例对象层级结构: 假设有如下自定义对象层级结构:
    class Address {
        var street: String?
    }
    class Person {
        var address: Address?
    }
    class Company {
        var ceo: Person?
    }
    
  2. 使用KVC访问并修改深层属性
    let company = Company()
    let person = Person()
    let address = Address()
    address.street = "123 Main St"
    person.address = address
    company.ceo = person
    
    if let street = company.value(forKeyPath: "ceo.address.street") as? String {
        print("Original street: \(street)")
    }
    company.setValue("456 Elm St", forKeyPath: "ceo.address.street")
    if let newStreet = company.value(forKeyPath: "ceo.address.street") as? String {
        print("New street: \(newStreet)")
    }
    

异常情况及处理方式

  1. 键不存在异常
    • 当访问或设置一个不存在的键时,会抛出NSUndefinedKeyException异常。
    • 处理方式:
      • 在访问属性前,可以先使用responds(to:)方法检查对象是否响应对应的KVC操作。例如:
        if company.responds(to: #selector(NSObject.value(forKeyPath:))) {
            if let street = company.value(forKeyPath: "ceo.address.street") as? String {
                print("Street: \(street)")
            }
        }
        
      • 可以重写valueForUndefinedKey(_:)setValue:forUndefinedKey:方法来自定义处理逻辑,避免抛出异常。例如:
        class Company {
            var ceo: Person?
            override func valueForUndefinedKey(_ key: String) -> Any? {
                return nil
            }
            override func setValue(_ value: Any?, forUndefinedKey key: String) {
                // 自定义处理逻辑,比如记录日志等
            }
        }
        
  2. 类型不匹配异常
    • 当设置的值与属性的预期类型不匹配时,会抛出NSInvalidArgumentException异常。
    • 处理方式:在设置值之前,进行类型检查。例如:
      let newStreetValue = "789 Oak St"
      if let street = company.value(forKeyPath: "ceo.address.street") as? String {
          if type(of: newStreetValue) == type(of: street) {
              company.setValue(newStreetValue, forKeyPath: "ceo.address.street")
          } else {
              print("Type mismatch. Cannot set value.")
          }
      }