Swift 5
要使用纯 Swift 类型设置和获取属性值,可以使用内部 ReflectionMirror.swift 方法与共享函数:
swift_reflectionMirror_recursiveCount
swift_reflectionMirror_recursiveChildMetadata
swift_reflectionMirror_recursiveChildOffset
其思想是获得有关对象的每个属性的信息,然后通过其指针偏移量将值设置为所需值。
这里有一个使用 Swift 的 KeyValueCoding
协议实现了 setValue(_ value: Any?, forKey key: String)
方法的示例代码:
typealias NameFreeFunc = @convention(c) (UnsafePointer<CChar>?) -> Void
struct FieldReflectionMetadata {
let name: UnsafePointer<CChar>? = nil
let freeFunc: NameFreeFunc? = nil
let isStrong: Bool = false
let isVar: Bool = false
}
@_silgen_name("swift_reflectionMirror_recursiveCount")
fileprivate func swift_reflectionMirror_recursiveCount(_: Any.Type) -> Int
@_silgen_name("swift_reflectionMirror_recursiveChildMetadata")
fileprivate func swift_reflectionMirror_recursiveChildMetadata(
_: Any.Type
, index: Int
, fieldMetadata: UnsafeMutablePointer<FieldReflectionMetadata>
) -> Any.Type
@_silgen_name("swift_reflectionMirror_recursiveChildOffset")
fileprivate func swift_reflectionMirror_recursiveChildOffset(_: Any.Type, index: Int) -> Int
protocol Accessors {}
extension Accessors {
static func set(value: Any?, pointer: UnsafeMutableRawPointer) {
if let value = value as? Self {
pointer.assumingMemoryBound(to: self).pointee = value
}
}
}
struct ProtocolTypeContainer {
let type: Any.Type
let witnessTable = 0
var accessors: Accessors.Type {
unsafeBitCast(self, to: Accessors.Type.self)
}
}
protocol KeyValueCoding {
}
extension KeyValueCoding {
private mutating func withPointer<Result>(displayStyle: Mirror.DisplayStyle, _ body: (UnsafeMutableRawPointer) throws -> Result) throws -> Result {
switch displayStyle {
case .struct:
return try withUnsafePointer(to: &self) {
let pointer = UnsafeMutableRawPointer(mutating: $0)
return try body(pointer)
}
case .class:
return try withUnsafePointer(to: &self) {
try $0.withMemoryRebound(to: UnsafeMutableRawPointer.self, capacity: 1) {
try body($0.pointee)
}
}
default:
fatalError("Unsupported type")
}
}
public mutating func setValue(_ value: Any?, forKey key: String) {
let mirror = Mirror(reflecting: self)
guard let displayStyle = mirror.displayStyle
, displayStyle == .class || displayStyle == .struct
else {
return
}
let type = type(of: self)
let count = swift_reflectionMirror_recursiveCount(type)
for i in 0..<count {
var field = FieldReflectionMetadata()
let childType = swift_reflectionMirror_recursiveChildMetadata(type, index: i, fieldMetadata: &field)
defer { field.freeFunc?(field.name) }
guard let name = field.name.flatMap({ String(validatingUTF8: $0) }),
name == key
else {
continue
}
let clildOffset = swift_reflectionMirror_recursiveChildOffset(type, index: i)
try? withPointer(displayStyle: displayStyle) { pointer in
let valuePointer = pointer.advanced(by: clildOffset)
let container = ProtocolTypeContainer(type: childType)
container.accessors.set(value: value, pointer: valuePointer)
}
break
}
}
}
这种方法适用于
class
和
struct
,并支持可选、枚举和继承(对于类)属性:
enum UserType {
case admin
case guest
case none
}
class User: KeyValueCoding {
let id = 0
let name = "John"
let birthday: Date? = nil
let type: UserType = .none
}
var user = User()
user.setValue(12345, forKey: "id")
user.setValue("Bob", forKey: "name")
user.setValue(Date(), forKey: "birthday")
user.setValue(UserType.admin, forKey: "type")
print(user.id, user.name, user.birthday!, user.type)
struct Book: KeyValueCoding {
let id = 0
let title = "Swift"
let info: String? = nil
}
var book = Book()
book.setValue(56789, forKey: "id")
book.setValue("ObjC", forKey: "title")
book.setValue("Development", forKey: "info")
print(book.id, book.title, book.info!)
如果您害怕使用
@_silgen_name
来共享函数,您可以通过
dlsym
动态访问它,例如:
dlsym(RTLD_DEFAULT, "swift_reflectionMirror_recursiveCount")
等。
更新
有一个Swift包(https://github.com/ikhvorost/KeyValueCoding),完全实现了纯Swift的KeyValueCoding
协议,并支持:通过键获取/设置任何属性的值、下标、获取元数据类型、属性列表等功能。
reflect
函数和MirrorType来获取值,但你不知道苹果在后台是如何做到的。他们可以在运行时以某种方式迭代属性,直到他们发布源代码,我们才会知道。 - s1ddok