反射Mirror
反射就是可以动态获取类型、成员信息,在运行时可以调用方法、属性等行为的特性。对于纯Swift类来说,并不支持像OC那样操作;但是Swift标准库仍然提供了反射机制让我们访问成员信息。
class HotpotCat: NSObject {
var age: Int = 18
var height = 1.85
}
var hotpot = HotpotCat()
let mirror = Mirror(reflecting: hotpot)
//children 是个元组 typealias Mirror.Child = (label: String?, value: Any)
for pro in mirror.children {
print("\(pro.label!):\(pro.value)")
}
age:18
height:1.85
那么是不是可以写个JSON解析?
protocol JSONMap {
func jsonParse() -> Any
}
extension JSONMap {
func jsonParse() -> Any {
let mirror = Mirror(reflecting: self)
//递归调用出口
guard !mirror.children.isEmpty else { return self }
var keyValue: [String : Any] = [:]
for children in mirror.children {
//是否遵守协议
if let value = children.value as? JSONMap {
if let keyName = children.label {
//递归逐层解析数据
keyValue[keyName] = value.jsonParse()
} else {
print("label is nil")
}
} else {
print("not conform JSONMap protocol")
}
}
return keyValue
}
}
调用
//遵守协议,不然无法调用jsonParse
extension Int: JSONMap {}
extension Double: JSONMap {}
class HotpotCat: JSONMap {
var age: Int = 18
var height = 1.85
}
var hotpot = HotpotCat()
print(hotpot.jsonParse())
["height": 1.85, "age": 18]
错误处理
Error协议
上面的例子中,错误直接打印了出来,接着处理下错误。
Swift
提供了Error
协议来标识当前应用程序发生错误的情况,Error
定义如下:
public protocol Error {
}
无论是struct
、Class
还是enum
都可以通过遵循这个协议来表示错误。
上面的解析错误可以简单表示如下:
enum JSONMapError: Error {
case emptyKey
case notConformProtocol
}
json解析改完错误后如下(为了区分返回值把error
抛出):
protocol JSONMap {
func jsonParse() throws -> Any
}
extension JSONMap {
func jsonParse() throws -> Any {
let mirror = Mirror(reflecting: self)
//递归调用出口
guard !mirror.children.isEmpty else { return self }
var keyValue: [String : Any] = [:]
for children in mirror.children {
//是否遵守协议
if let value = children.value as? JSONMap {
if let keyName = children.label {
//递归逐层解析数据
keyValue[keyName] = try value.jsonParse()//try错误抛出给上层
} else {
throw JSONMapError.emptyKey
}
} else {
throw JSONMapError.notConformProtocol
}
}
return keyValue
}
}
try
使用try
关键字,这个是最简单的,可以简单理解为甩锅
。
try
&try?
&try!
-
try
:抛出给上层,向上抛出。上层无法处理直接crash。 -
try?
:返回可选类型,要么成功返回值,要么错误返回nil,这里不关心具体的错误。 -
try!
:表示绝对自信,这行代码不会发生错误。
do...catch
可以捕获具体错误信息。
增加一个name
属性,不遵守JSONMap
协议
class HotpotCat: JSONMap {
var age: Int = 18
var height = 1.85
var name: String = "hotpot"
}
var hotpot = HotpotCat()
print(try? hotpot.jsonParse())
do {
try hotpot.jsonParse()
} catch {
print(error)
}
nil
notConformProtocol
LocalizedError协议
上面的error
信息还是比较粗糙的,如果想要更详细的信息可以使用LocalizedError
协议,定义如下:
public protocol LocalizedError : Error {
/// A localized message describing what error occurred.
var errorDescription: String? { get }
/// A localized message describing the reason for the failure.
var failureReason: String? { get }
/// A localized message describing how one might recover from the failure.
var recoverySuggestion: String? { get }
/// A localized message providing "help" text if the user requests help.
var helpAnchor: String? { get }
}
可以实现协议,将错误表达的更详尽:
extension JSONMapError: LocalizedError {
var errorDescription: String? {
switch self {
case .emptyKey:
return NSLocalizedString("The Object doesn't has children", comment: "JSONMap object has empty children")
case .notConformProtocol:
return NSLocalizedString("The Object doesn't conform JSONMap protocol", comment: "Don't conform json map")
}
}
}
调用:
var hotpot = HotpotCat()
do {
try hotpot.jsonParse()
} catch {
print(error.localizedDescription)
print("\(String(describing: (error as? LocalizedError)?.errorDescription))")
}
输出
The Object doesn't conform JSONMap protocol
Optional("The Object doesn\'t conform JSONMap protocol")
CustomNSError
与oc NSError
对应的有一个CustomNSError
,有三个默认属性
- a static errorDomain
- an errorCode integer
- an errorUserInfo dictionary
public protocol CustomNSError : Error {
/// The domain of the error.
static var errorDomain: String { get }
/// The error code within the given domain.
var errorCode: Int { get }
/// The user-info dictionary.
var errorUserInfo: [String : Any] { get }
}
实现:
extension JSONMapError: CustomNSError {
var errorCode: Int {
switch self {
case .emptyKey:
return 404
case .notConformProtocol:
return 504
}
}
}
调用:
var hotpot = HotpotCat()
do {
try hotpot.jsonParse()
} catch {
print(error.localizedDescription)
print("\(String(describing: (error as? LocalizedError)?.errorDescription))")
print((error as? CustomNSError)?.errorCode as Any)
}
输出:
The Object doesn't conform JSONMap protocol
Optional("The Object doesn\'t conform JSONMap protocol")
Optional(504)
完整的解析代码:
protocol JSONMap {
func jsonParse() throws -> Any
}
extension JSONMap {
func jsonParse() throws -> Any {
let mirror = Mirror(reflecting: self)
//递归调用出口
guard !mirror.children.isEmpty else { return self }
var keyValue: [String : Any] = [:]
for children in mirror.children {
//是否遵守协议
if let value = children.value as? JSONMap {
if let keyName = children.label {
//递归逐层解析数据
keyValue[keyName] = try value.jsonParse()//try错误抛出给上层
} else {
throw JSONMapError.emptyKey
}
} else {
throw JSONMapError.notConformProtocol
}
}
return keyValue
}
}
//遵守协议,不然无法调用jsonParse
extension Int: JSONMap {}
extension Double: JSONMap {}
extension String: JSONMap {}
enum JSONMapError: Error {
case emptyKey
case notConformProtocol
}
extension JSONMapError: LocalizedError {
var errorDescription: String? {
switch self {
case .emptyKey:
return NSLocalizedString("The Object doesn't has children", comment: "JSONMap object has empty children")
case .notConformProtocol:
return NSLocalizedString("The Object doesn't conform JSONMap protocol", comment: "Don't conform json map")
}
}
}
extension JSONMapError: CustomNSError {
var errorCode: Int {
switch self {
case .emptyKey:
return 404
case .notConformProtocol:
return 504
}
}
}
class HotpotCat: JSONMap {
var age: Int = 18
var height = 1.85
var name: String = "hotpot"
}
var hotpot = HotpotCat()
do {
let jsonData = try hotpot.jsonParse()
print(jsonData)
} catch {
print(error.localizedDescription)
print("\(String(describing: (error as? LocalizedError)?.errorDescription))")
print((error as? CustomNSError)?.errorCode as Any)
}
//输出
["age": 18, "height": 1.85, "name": "hotpot"]
Mirror源码解析
系统是如何通过Mirror
拿到对应的value
值?oc
中通过runtime
很容易做到。Swift
是一门静态型语言,系统在底层做了什么样的事情,才让Swift
有了反射的特性。
Mirror.swift
public struct Mirror {
public init(reflecting subject: Any) {
if case let customized as CustomReflectable = subject {
self = customized.customMirror
} else {
self = Mirror(internalReflecting: subject)
}
}
}
- 可以看到
Mirror
是一个结构体 - 有一个初始化方法,传进来是一个
Any
接着看下ReflectionMirror.swift
的实现
- 通过
_getNormalizedType
获取subjectType
- 通过
_getChildCount
获取childCount
属性大小 - 遍历将属性拼到集合当中
_getNormalizedType
_getNormalizedType
查找传进来的动态类型(真正类型):
@_silgen_name("swift_reflectionMirror_normalizedType")
internal func _getNormalizedType(_: T, type: Any.Type) -> Any.Type
@_silgen_name
关键字
在.c
文件中写一个c
方法,.h
中不暴露方法声明。int testAdd(int a, int b) { return a + b; }
在
swift
文件中调用@_silgen_name("testAdd") func swift_testAdd(a: Int32, b: Int32) -> Int32 var resut = swift_testAdd(a: 20, b: 30) print(resut)//50
只在
Swift
中进行了方法声明,就调用到了c
方法。相当于swift_testAdd
调用了testAdd
方法。
这里说明_getNormalizedType
方法最终会调用swift_reflectionMirror_normalizedType
方法,搜索下发现swift_reflectionMirror_normalizedType
方法在ReflectionMirror.mm
中:
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
const Metadata *swift_reflectionMirror_normalizedType(OpaqueValue *value,
const Metadata *type,
const Metadata *T) {
return call(value, T, type, [](ReflectionMirrorImpl *impl) { return impl->type; });
}
-
value
就是Mirror
实例 -
type
就是type(of:)
获取的type
-
T
就是传进来的类型Mirror(reflecting: hotpot)
- 返回值是
impl->type
,结构是ReflectionMirrorImpl
call
方法
template
auto call(OpaqueValue *passedValue, const Metadata *T, const Metadata *passedType,
const F &f) -> decltype(f(nullptr))
{
//
const Metadata *type;
OpaqueValue *value;
std::tie(type, value) = unwrapExistential(T, passedValue);
//传进来的type不为空,type = passedType
if (passedType != nullptr) {
type = passedType;
}
unwrapExistential
方法最终会去metadata
中找type,以struct
为类分析下TargetStructMetadata
->TargetValueMetadata(Description)
->TargetMetadata(kind)
。
ConstTargetMetadataPointer
getDescription() const {
return Description;
}
Description
记录的就是有关于元数据的描述,TargetValueTypeDescriptor -> TargetTypeContextDescriptor(name) -> TargetContextDescriptor(Flags、Parent)
TargetTypeContextDescriptor
/// The name of the type.
//这里name记录的就是Int、String、Hotpot类型
TargetRelativeDirectPointer Name;
还原下Struct metadata
:
struct StructMetadata {
var kind: Int
var desc: UnsafeRawPointer
}
desc
结构是什么呢?
TargetValueTypeDescriptor -> TargetTypeContextDescriptor(name) -> TargetContextDescriptor(Flags、Parent)
name
是TargetRelativeDirectPointer -> TargetRelativeDirectPointer -> RelativeDirectPointer -> RelativeDirectPointerImpl
RelativeDirectPointerImpl
结构如下:
template
class RelativeDirectPointerImpl {
private:
Offset RelativeOffset;//int32_t
//RelativeDirectPointer中有两个方法
operator typename super::PointerTy() const & {
return this->get();
}
const typename super::ValueTy *operator->() const & {
return this->get();
}
};
PointerTy
和ValueTy
对应如下,为传进来的T
using ValueTy = T;
using PointerTy = T*;
两个方法都调用了this->get()
PointerTy get() const & {
// Check for null.
if (Nullable && RelativeOffset == 0)
return nullptr;
// The value is addressed relative to `this`.
uintptr_t absolute = detail::applyRelativeOffset(this, RelativeOffset);
return reinterpret_cast(absolute);
}
在this->get()
中调用了applyRelativeOffset
方法
template
static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) {
static_assert(std::is_integral::value &&
std::is_signed::value,
"offset type should be signed integer");
auto base = reinterpret_cast(basePtr);
// We want to do wrapping arithmetic, but with a sign-extended
// offset. To do this in C, we need to do signed promotion to get
// the sign extension, but we need to perform arithmetic on unsigned values,
// since signed overflow is undefined behavior.
auto extendOffset = (uintptr_t)(intptr_t)offset;
return base + extendOffset;
}
返回值为base
+extendOffset
。这里是一个相对指针,本来指针里面存储一个地址,现在存储了相对地址。拿到自己的地址+地址偏移。存储的实际是'地址偏移'。
还原后desc
和name
如下:
struct StructMetadataDesc {
var flags: Int32
var parent: Int32
var name: RelativeDirectPointer
}
struct RelativeDirectPointer {
var offset: Int32
//由于要操作self 加上 mutating
mutating func get() -> UnsafeMutablePointer {
let offset = self.offset
// 模拟this + offset
//获取self指针
return withUnsafePointer(to: &self) { p in
//为了指针相加,先转成raw pointer再offset绑定到T.self再转回UnsafeMutablePointer
return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self))
}
}
}
测试下:
struct Hotpot {
var age = 18
var name = "hotpot"
}
var h = Hotpot()
var p = Hotpot.self
//unsafeBitCast 按位强转 Hotpot.self 到 StructMetadata。8字节按位存储。不安全。
let ptr = unsafeBitCast(Hotpot.self as Any.Type, to: UnsafeMutablePointer.self)
//获取name。
/**
1.ptr.pointer获取StructMetadata指针。
2.desc.pointee获取desc指针。
3.name获取type名称指针。
4.最终通过get方法找到偏移地址存放的结构体名称。
*/
let namePtr = ptr.pointee.desc.pointee.name.get()
print(String(cString: namePtr))
输出:
Hotpot
这里就拿到了结构体的名称。
原理就是找到metadata->desc->desc中的内容
上面的分析总结下就是下面一张图:
获取属性名和值
回到上面分析了ReflectionMirror.swift
文件subjectType
,在ReflectionMirror.mm
文件中call
函数
switch (type->getKind()) {
case MetadataKind::Tuple: {
TupleImpl impl;
return call(&impl);
}
case MetadataKind::Struct: {
StructImpl impl;
return call(&impl);
}
case MetadataKind::Enum:
case MetadataKind::Optional: {
EnumImpl impl;
return call(&impl);
}
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass:
case MetadataKind::Class: {
return callClass();
}
case MetadataKind::Metatype:
case MetadataKind::ExistentialMetatype: {
MetatypeImpl impl;
return call(&impl);
}
可以看到根据不同类型传入不同的impl
,这里struct
传入的是StructImpl
:
// Implementation for structs.
struct StructImpl : ReflectionMirrorImpl {
//找到 Description->isReflectable 是否可反射
bool isReflectable() {
const auto *Struct = static_cast(type);
const auto &Description = Struct->getDescription();
return Description->isReflectable();
}
//
char displayStyle() {
return 's';
}
//找到 Struct->getDescription()->NumFields
intptr_t count() {
if (!isReflectable()) {
return 0;
}
auto *Struct = static_cast(type);
return Struct->getDescription()->NumFields;//属性的count
}
可以看到也是从desc
中获取。
TargetTypeContextDescriptor
中其它字段如下:
TargetRelativeDirectPointer Name;
TargetRelativeDirectPointer AccessFunctionPtr;
TargetRelativeDirectPointer Fields;
TargetValueTypeDescriptor
中其它字段如下:
uint32_t NumFields;
uint32_t FieldOffsetVectorOffset;
这时候还原后的desc
如下:
struct StructMetadataDesc {
var flags: Int32
var parent: Int32
var name: RelativeDirectPointer //存放type的名称
var accessFunctionPtr: RelativeDirectPointer
var fields: RelativeDirectPointer
var numFields: UInt32
var fieldOffsetVectorOffset: UInt32
}
调用
print(ptr.pointee.desc.pointee.numFields)
输出
2
可以看到有2个属性。
ReflectionMirror.mm
中还有一个方法subscript
它用来获取属性名称和值。
//获取属性偏移值
intptr_t childOffset(intptr_t i) {
//获取metadata
auto *Struct = static_cast(type);
if (i < 0 || (size_t)i > Struct->getDescription()->NumFields)
swift::crash("Swift mirror subscript bounds check failure");
// Load the offset from its respective vector.
return Struct->getFieldOffsets()[i];
}
const FieldType childMetadata(intptr_t i, const char **outName,
void (**outFreeFunc)(const char *)) {
StringRef name;
FieldType fieldInfo;
std::tie(name, fieldInfo) = getFieldAt(type, i);
assert(!fieldInfo.isIndirect() && "indirect struct fields not implemented");
*outName = name.data();
*outFreeFunc = nullptr;
return fieldInfo;
}
//获取属性名称和值
AnyReturn subscript(intptr_t i, const char **outName,
void (**outFreeFunc)(const char *)) {
//属性的名称
auto fieldInfo = childMetadata(i, outName, outFreeFunc);
//当前的value
auto *bytes = reinterpret_cast(value);
//offset
auto fieldOffset = childOffset(i);
//属性的值 = value + offset
auto *fieldData = reinterpret_cast(bytes + fieldOffset);
return copyFieldContents(fieldData, fieldInfo);
}
getFieldAt
方法获取属性名
static std::pair
getFieldAt(const Metadata *base, unsigned index) {
using namespace reflection;
// If we failed to find the field descriptor metadata for the type, fall
// back to returning an empty tuple as a standin.
auto failedToFindMetadata = [&]() -> std::pair {
auto typeName = swift_getTypeName(base, /*qualified*/ true);
missing_reflection_metadata_warning(
"warning: the Swift runtime found no field metadata for "
"type '%*s' that claims to be reflectable. Its fields will show up as "
"'unknown' in Mirrors\n",
(int)typeName.length, typeName.data);
return {"unknown", FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))};
};
//获取desc
auto *baseDesc = base->getTypeContextDescriptor();
if (!baseDesc)
return failedToFindMetadata();
//通过desc->Fields属性的get方法获取fields
auto *fields = baseDesc->Fields.get();
if (!fields)
return failedToFindMetadata();
//获取field
auto &field = fields->getFields()[index];
// Bounds are always valid as the offset is constant.
//通过field getFieldName获取name,这里就拿到了属性值的名称
auto name = field.getFieldName();
Fields
结构如下:
是一个相对指针,存储的是
FieldDescriptor
:
class FieldDescriptor {
const FieldRecord *getFieldRecordBuffer() const {
return reinterpret_cast(this + 1);
}
public:
const RelativeDirectPointer MangledTypeName;
const RelativeDirectPointer Superclass;
FieldDescriptor() = delete;
const FieldDescriptorKind Kind;
const uint16_t FieldRecordSize;
const uint32_t NumFields;
还原后:
struct FieldDescriptor {
var MangledTypeName: RelativeDirectPointer
var Superclass: RelativeDirectPointer
var kind: UInt16
var FieldRecordSize: UInt16
var NumFields: UInt32
var fields: FieldRecord //连续的存储空间,记录属性字段
}
getFieldName
在Records.h
中
StringRef getFieldName() const {
return FieldName.get();
}
FieldName
在FieldRecord
中:
class FieldRecord {
const FieldRecordFlags Flags;
public:
const RelativeDirectPointer MangledTypeName;
const RelativeDirectPointer FieldName;
FieldRecord() = delete;
bool hasMangledTypeName() const {
return MangledTypeName;
}
StringRef getMangledTypeName() const {
return Demangle::makeSymbolicMangledNameStringRef(MangledTypeName.get());
}
StringRef getFieldName() const {
return FieldName.get();
}
bool isIndirectCase() const {
return Flags.isIndirectCase();
}
};
还原下:
struct FieldRecord {
var Flags: Int32
var MangledTypeName: RelativeDirectPointer
var FieldName: RelativeDirectPointer
}
这个时候就可以去取到属性名称了
//获取FieldDescriptor
let fieldDescriptorPtr = ptr.pointee.desc.pointee.fields.get()
//获取recordPtr
let recordPtr = withUnsafePointer(to: &fieldDescriptorPtr.pointee.fields){
// 先把fields转为UnsafeRawPointer,然后移动
return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to:FieldRecord.self).advanced(by:0))
}
//获取`age`名称,
print(String(cString: recordPtr.pointee.FieldName.get()))
age
总结:
Mirror
获取metadata->desc
- 通过
desc->name
获取类型 - 通过
desc->numFields
获取属性个数 - 用过
desc->fields->FieldDescriptor(fields)获取属性描述->FieldName
获取属性名
demo
参考
https://www.sohu.com/a/275678142_470093
https://swift.gg/2018/11/15/how-mirror-works/