@dynamicMemberLookup 是 Swift 的一个注解,允许通过下标在运行时重写对对象属性的访问。历史上,此机制是为更好地与像 Python 这样的动态语言集成而引入的,并且旨在更灵活地路由对代理对象、动态模型和 DSL 中的数据的访问。
问题:标准 Swift 要求明确声明属性,并在编译时检查它们是否存在。但有时需要按名称访问在编译时不存在的属性,例如处理 JSON、API、代理对象和脚本包装时。
解决方案:使用 @dynamicMemberLookup 并实现 subscript(dynamicMember:) 以拦截尝试访问不存在的属性。
代码示例:
@dynamicMemberLookup struct JSON { private var data: [String: Any] subscript(dynamicMember member: String) -> Any? { data[member] } } let user = JSON(data: ["name": "Anna", "age": 23]) print(user.name as? String) // Anna
关键特点:
可以为类实现 dynamicMemberLookup 吗,还是只能为结构体?
是的,可以应用于类、结构体和枚举(从 Swift 5+ 开始)。重要的是实现相应的 subscript。
如果通过 dynamicMemberLookup 访问不存在的属性会怎样?
将从 subscript 返回值,缺少值的处理责任在于您的实现。
例如,在上面的示例中,如果访问 user.secret,将返回 nil。
可以使用其他类型的键进行动态访问,例如 Int 吗?
可以!可以声明带有其他参数标签的 subscript(dynamicMember:) 并与普通 subscript 结合使用。
@dynamicMemberLookup struct ArrayProxy { private let array: [Int] subscript(dynamicMember member: String) -> Int? { if member == "first" { return array.first } if member == "last" { return array.last } return nil } subscript(index: Int) -> Int? { array[safe: index] } }
开发者对普通用户模型(User)使用 @dynamicMemberLookup,以便通过字符串访问任何字段。代码变得不透明,IDE 失去了提示。
优点:
缺点:
@dynamicMemberLookup 用于处理任意 JSON 对象,其字段事先未知。该机制允许优雅且安全地(通过 Any?/optional binding)处理非结构化数据。
优点:
缺点: