Swift中类未定义时,如何创建可写入键路径数组?
这问题我之前也碰到过,核心问题出在你对AnyClass的理解和KeyPath类型匹配上,咱们一步步拆解解决:
首先先解释报错原因:
你写的WritableKeyPath<AnyClass, Double>里的AnyClass其实是类的元类型(比如Class1.self的类型),而你要操作的是类的实例属性(比如Class1实例的tag),这两者完全不是一回事。编译器自然会报错,因为元类型本身根本没有tag或者volume这些实例属性。
而且退一步说,就算你改成AnyObject(任意对象实例的类型),WritableKeyPath<AnyObject, Double>也没法直接容纳\Class1.tag这种具体类的KeyPath——Swift是强类型语言,AnyObject不保证有tag属性,所以这种类型转换是不被允许的。
接下来给两种可行的解决方案,根据你的场景选:
方案一:用协议统一类型(类型安全首选)
如果你的Class1、Class2都是你能修改的类,最稳妥的方式是定义一个协议,把需要编辑的Double属性都声明进去,然后让这些类遵循协议:
// 定义协议,声明需要可写的Double属性 protocol DoublePropertyEditable { var tag: Double { get set } var volume: Double { get set } } // 让你的类遵循协议 class Class1: DoublePropertyEditable { var tag: Double = 0.0 // 其他原有属性和方法... } class Class2: DoublePropertyEditable { var volume: Double = 0.0 // 其他原有属性和方法... }
然后你就可以用协议类型的KeyPath数组了,所有遵循协议的类都能兼容:
// 现在数组里的KeyPath都是针对协议类型的 var properties: [WritableKeyPath<DoublePropertyEditable, Double>] = [ \.tag, \.volume ] // 设置方法也改成接收协议类型的实例 func setProperty(keyIndex: Int, value: Double, on object: DoublePropertyEditable) { guard keyIndex < properties.count else { return } // 做个越界检查更安全 object[keyPath: properties[keyIndex]] = value }
调用示例:
let obj1 = Class1() setProperty(keyIndex: 0, value: 15.0, on: obj1) print(obj1.tag) // 输出 15.0 let obj2 = Class2() setProperty(keyIndex: 1, value: 6.0, on: obj2) print(obj2.volume) // 输出 6.0
这个方案的好处是完全类型安全,编译器会帮你检查所有遵循协议的类都实现了要求的属性,不会有运行时类型错误。
方案二:用闭包封装设置逻辑(无需修改原有类)
如果你的Class1、Class2是第三方库的类,没法修改它们来遵循协议,那可以用闭包来封装“设置值”的逻辑,把不同类的类型转换隐藏在闭包里:
// 数组里存的是闭包,每个闭包负责给对应类型的对象设置值 var propertySetters: [(AnyObject, Double) -> Void] = [] // 添加Class1的tag设置逻辑 propertySetters.append { obj, value in guard let class1Obj = obj as? Class1 else { return } class1Obj.tag = value } // 添加Class2的volume设置逻辑 propertySetters.append { obj, value in guard let class2Obj = obj as? Class2 else { return } class2Obj.volume = value } // 设置方法接收任意对象实例 func setProperty(keyIndex: Int, value: Double, on object: AnyObject) { guard keyIndex < propertySetters.count else { return } propertySetters[keyIndex](object, value) }
调用示例:
let obj1 = Class1() setProperty(keyIndex: 0, value: 25.0, on: obj1) print(obj1.tag) // 输出 25.0 let obj2 = Class2() setProperty(keyIndex: 1, value: 8.5, on: obj2) print(obj2.volume) // 输出 8.5
这个方案更灵活,但需要手动处理类型转换,如果传入的对象类型不对,闭包会直接忽略设置操作,适合无法修改原有类的场景。
内容的提问来源于stack exchange,提问作者Fischer

