Swift 5.7数组扩展KeyPath递归查找Identifiable元素失效问题
问题成因
- 代码中
e[keyPath: recursivePath] as [any Identifiable]的类型擦除操作,把原本强类型的具体元素数组转成了存在型(existential)数组[any Identifiable],此时数组元素类型是存在类型any Identifiable,而非原数组中遵循Identifiable协议的具体元素类型。 - 自定义数组扩展的约束是
Array where Element: Identifiable,要求数组元素是具体的、遵循Identifiable协议的类型,而存在类型any Identifiable本身是协议容器类型,不满足该泛型约束,因此无法匹配到自定义的递归下标,调用直接失败。 - 原下标未指定第一个参数的外部标签,调用时容易和系统默认的整数索引下标混淆,可读性较差。
正确实现
不需要做任何类型擦除,直接利用Swift强类型特性递归即可:recursivePath指向的子数组本身就是[Element]类型,天然满足扩展的泛型约束,可以直接调用自定义下标完成递归查找,兼容Swift 5.7、Xcode 14.0 beta (14A5228q)环境。
extension Array where Element: Identifiable { subscript(id targetId: Element.ID?, recursivePath: KeyPath<Element, Array<Element>>) -> Element? { guard let targetId = targetId else { return nil } for element in self { // 当前层命中直接返回 if element.id == targetId { return element } // 递归查找子节点,无需类型转换 if let matched = element[keyPath: recursivePath][id: targetId, recursivePath: recursivePath] { return matched } } return nil } }
使用示例
// 示例树节点 struct MenuItem: Identifiable { let id: String let name: String var subItems: [MenuItem] } // 构造测试树结构 let menuTree: [MenuItem] = [ MenuItem(id: "home", name: "首页", subItems: []), MenuItem(id: "settings", name: "设置", subItems: [ MenuItem(id: "account", name: "账号设置", subItems: [ MenuItem(id: "avatar", name: "修改头像", subItems: []), MenuItem(id: "password", name: "修改密码", subItems: []) ]), MenuItem(id: "notification", name: "通知设置", subItems: []) ]) ] // 递归查找id为"avatar"的节点 let targetItem = menuTree[id: "avatar", recursivePath: \.subItems] // 返回结果为name="修改头像"的MenuItem实例
实现说明:递归查找的时间复杂度为O(n),n为树结构的总节点数,无额外类型转换开销。
内容的提问来源于stack exchange,提问作者Marlo
相关产品推荐
相关产品推荐

