JAVA中的isMirror函数_Swift中的反射Mirror

Swift中的反射Mirror

[TOC]

前言

Mirror是Swift中的反射机制,对于C#和Java开发人员来说,应该很熟悉反射这个概念。反射就是可以动态的获取类型以及成员信息,同时也可以在运行时动态的调用方法和属性等。

对于iOS开发人员来说,入门时使用的Objective-C是很少强调反射概念的,因为OC的Runtime要比其他语言的反射强大的多。

另外,在阅读本篇文章前建议先看看我的另一篇文章:

Swift 中的类型

1. Mirror 简介

Mirror是Swift中的反射机制的实现,它的本质是一个结构体。其部分源码(Swift 5.3.1)如下:

public struct Mirror {

/// A suggestion of how a mirror's subject is to be interpreted.

///

/// Playgrounds and the debugger will show a representation similar

/// to the one used for instances of the kind indicated by the

/// `DisplayStyle` case name when the mirror is used for display.

public enum DisplayStyle {

case `struct`, `class`, `enum`, tuple, optional, collection

case dictionary, `set`

}

/// The static type of the subject being reflected.

///

/// This type may differ from the subject's dynamic type when this mirror

/// is the `superclassMirror` of another mirror.

public let subjectType: Any.Type

/// A collection of `Child` elements describing the structure of the

/// reflected subject.

public let children: Children

/// A suggested display style for the reflected subject.

public let displayStyle: DisplayStyle?

/// A mirror of the subject's superclass, if one exists.

public var superclassMirror: Mirror? {

return _makeSuperclassMirror()

}

}

subjectType:表示类型,被反射主体的类型

children:子元素集合

displayStyle:显示类型,基本类型为nil 枚举值: struct, class, enum, tuple, optional, collection, dictionary, set

superclassMirror:父类反射, 没有父类为nil

除了这些属性还有一些初始化方法,我们最常用的就是初始化方法就是:

/// Creates a mirror that reflects on the given instance.

///

/// If the dynamic type of `subject` conforms to `CustomReflectable`, the

/// resulting mirror is determined by its `customMirror` property.

/// Otherwise, the result is generated by the language.

///

/// If the dynamic type of `subject` has value semantics, subsequent

/// mutations of `subject` will not observable in `Mirror`. In general,

/// though, the observability of mutations is unspecified.

///

/// - Parameter subject: The instance for which to create a mirror.

public init(reflecting subject: Any) {

if case let customized as CustomReflectable = subject {

self = customized.customMirror

} else {

self = Mirror(internalReflecting: subject)

}

}

根据源码我们还可以看到能够使用customMirror,这个没太研究,在源码中可以看到很多CustomMirror的身影,感兴趣的可以去研究研究。对于非customMirror的统一使用Mirror(internalReflecting:)进行初始化。

关于customMirror的补充,摘抄自swiftGG Mirror 的工作原理。

Mirror 允许类型用遵循 CustomReflectable 协议的方式提供一个自定义的表示方式。这给那些想表示得比内建形式更友好的类型提供一种有效的方法。 比如 Array 类型遵守 CustomReflectable 协议并且暴露其中的元素为无标签的 Children。Dictionary 使用这种方法暴露其中的键值对为带标签的 Children。

2. Mirror的简单使用

2.1 基本使用

这里我们通过使用Mirror打印对象的属性名称和属性值。

class Person {

var name: String = "xiaohei"

var age: Int = 18

var height = 1.85

}

var p = Person()

var mirror = Mirror(reflecting: p.self)

print("对象类型:\(mirror.subjectType)")

print("对象属性个数:\(mirror.children.count)")

print("对象的属性及属性值")

for child in mirror.children {

print("\(child.label!)---\(child.value)")

}

打印结果:

a4ca25caa6c9

image

我们可以看到,属性名称和值都已经正常打印。

2.2 将对象转换为字典

首先我们来体验一下将对象转换为字典。

class Animal {

var name: String?

var color: String?

private var birthday: Date = Date(timeIntervalSince1970: 0)

}

class Cat: Animal {

var master = "小黑"

var like: [String] = ["mouse", "fish"]

override init() {

super.init()

color = "黄色"

}

}

func mapDic(mirror: Mirror) -> [String: Any] {

var dic: [String: Any] = [:]

for child in mirror.children {

// 如果没有labe就会被抛弃

if let label = child.label {

let propertyMirror = Mirror(reflecting: child.value)

print(propertyMirror)

dic[label] = child.value

}

}

// 添加父类属性

if let superMirror = mirror.superclassMirror {

let superDic = mapDic(mirror: superMirror)

for p in superDic {

dic[p.key] = p.value

}

}

return dic

}

// Mirror使用

let cat = Cat()

cat.name = "大橘为重"

let mirror = Mirror(reflecting: cat)

let mirrorDic = mapDic(mirror: mirror)

print(mirrorDic)

打印结果:

a4ca25caa6c9

image

通过打印结果我们可以看到,对于一些基本类型,已经可选类型的数据都已经转换为字典值,对于私有属性也可以完成转换。如果里面包含类则还需要进行递归处理。

2.3 转 JSON

注: 这里并没有真正的转换成json字符串,还是只转换成了字典,重要在思想,如果需要转换成json还需要很多优化,以及特殊字符串的考量。

其实提到反射我们想到最多的应该就是JSON了,这里我们利用Mirror的特性,将对象转换成字典,对基本类型和类做了相应的处理,体会一下转json的思路。

首先我们定义一个Person对象,代码如下:

struct Person {

var name: String = "xiaohei"

var age: Int = 18

var isMale: Bool = true

var address: Address? = Address(street: "xizhimen North")

var height = 1.85

var like: Array = ["eat", "sleep", "play"]

var weight: Float = 75.0

var some: Int?

}

struct Address {

var street: String

}

// 创建一个Person对象

let p = Person()

为了通用性,我们可以编写一个协议,用来为所有类型提供转换的方法,只需要遵守该协议就可以使用协议中的方法。

//可以转换为 Json 的协议

protocol CustomJSONProtocol {

func toJSON() throws -> Any?

}

协议的实现过程中会有些错误,我们也简单的定义个枚举,方便处理。为了更加详细的描述错误信息,我们添加了错误描述和错误code。

// 转 json 时的错误类型

enum JSONMapError: Error{

case emptyKey

case notConformProtocol

}

// 错误描述

extension JSONMapError: LocalizedError{

var errorDescription: String?{

switch self {

case .emptyKey:

return "key 为空"

case .notConformProtocol:

return "没遵守协议"

}

}

}

// errorcode

extension JSONMapError: CustomNSError{

var errorCode: Int{

switch self {

case .emptyKey:

return 100

case .notConformProtocol:

return 101

}

}

}

协议实现的代码:

extension CustomJSONProtocol {

func toJSON() throws -> Any? {

//创建 Mirror 类型

let mirror = Mirror(reflecting: self)

// 如果没有属性,比如一般类型String、Int等,直接返回自己

guard !mirror.children.isEmpty else { return self }

var result: [String:Any] = [:]

// 遍历

for children in mirror.children {

if let value = children.value as? CustomJSONProtocol{

if let key = children.label {

print(key)

result[key] = try value.toJSON()

} else {

throw JSONMapError.emptyKey

}

} else {

throw JSONMapError.notConformProtocol

}

}

return result

}

}

将用到的类型都遵守协议

//将一般类型都遵从 CustomJSONProtocol 协议

extension Person: CustomJSONProtocol {}

extension String: CustomJSONProtocol {}

extension Int: CustomJSONProtocol {}

extension Bool: CustomJSONProtocol {}

extension Double: CustomJSONProtocol {}

extension Float: CustomJSONProtocol {}

extension Address: CustomJSONProtocol {}

// 数组需要单独处理,要不然就会报错emptyKey

extension Array: CustomJSONProtocol {

func toJSON() throws -> Any? {

return self

}

}

//Optionai 需要特别对待,原因是如果直接返回,则会是 .Some: [...]

extension Optional: CustomJSONProtocol {

func toJSON() throws -> Any? {

if let x = self {

if let value = x as? CustomJSONProtocol {

return try value.toJSON()

}

throw JSONMapError.notConformProtocol

}

return nil

}

}

最后我们打印一下:

do {

print(try p.toJSON()!)

} catch {

print(error.localizedDescription)

print((error as? JSONMapError)?.errorCode)

}

打印结果:

a4ca25caa6c9

image

我们看到,对于some这空值,并没有存储到字典中,因为swift中的字典对于空值是删除的意思。

如果想将其转换成json还需修改"[]"为"{}",这个对于数组和对象还不好区分,另外对于json字符串内的一些value也有可能是应一串json还需要添加转义字符等。

所以总的来说,思路是这样的,要想真正的做成通用的转json的方案还需要很多的优化,比如说,我们不可能将所有的基本类型都去遵守一个协议,这时候我们也可以考虑使用泛型去作为方法的参数。

3. Mirror 源码解析

源码版本Swift 5.3.1

在本章节我们将分析Mirror的部分源码,查看其底层实现,最后通过Swift代码使用内存重绑定的形式,仿写一下Mirror,来更好的探索Mirror的原理,理解Mirror的思想。

我们知道Swift是一门静态语言,那么在底层是如何实现的获取对应的属性值的呢?又或者说Swift的反射特性是如何实现的呢?下面我们通过对Mirror底层源码的探索来寻找答案。

3.1 代码结构

Mirror的实现是由一部分Swift代码加上另一部分C++代码。Swift代码实现在ReflectionMirror.swift文件中,C++代码实现在ReflectionMirror.mm文件中。Swift更适合用在实现更Swift的接口,但是在Swift中不能直接访问C++的类。这里使用了@_silgen_name来实现Swift调用C++中的方法。举个例子:

@_silgen_name("swift_reflectionMirror_count")

internal func _getChildCount(_: T, type: Any.Type) -> Int

@_silgen_name修饰符会通知Swift编译器将这个函数映射成swift_reflectionMirror_count符号,而不是Swift通常对应到的_getChildCount方法名修饰。需要注意的是,最前面的下划线表示这个修饰是被保留在标准库中的。在C++这边,这个函数是这样的。

// func _getChildCount(_: T, type: Any.Type) -> Int

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API

intptr_t swift_reflectionMirror_count(OpaqueValue *value,

const Metadata *

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值