Swift 反射Mirror的使用
反射(Reflection)介绍
对于C#、Java开发人员来说,肯定都对反射这个概念相当熟悉。所谓反射就是可以动态获取类型、成员信息,同时在运行时(而非编译时)可以动态调用任意方法、属性等行为的特性。
Objective-C 的 Runtime
在使用ObjC开发时很少强调其反射概念,因为ObjC的Runtime要比其他语言中的反射强大的多。在ObjC中可以很简单的实现字符串和类型的转换(NSClassFromString()),实现动态方法调用(performSelector: withObject:),动态赋值(KVC)等等。
Swift中的反射
在Swift中并不提倡使用Runtime,而是像其他语言一样使用反射(Reflect)。当然,目前Swift中的反射还没有其他语言中的反射功能强大,不仅远不及OC的Runtime,离Java的反射也有一定的距离。
Swift的反射机制是基于一个叫 Mirror 的 struct 来实现的,其内部有如下属性和方法:
/// 属性 Mirror.Children (label: String?, value: Any)
let children: Mirror.Children
/// 自定义反射
var customMirror: Mirror
/// 反射描述 一般为 Mirror for 类型
var description: String
/// 显示类型,基本类型为nil 枚举值: class, enum , struce, dictionary, array, set, tuple
let displayStyle: Mirror.DisplayStyle?
/// 类型
let subjectType: Any.Type
/// 父类反射, 没有父类为nil
var superclassMirror: Mirror?
通常获取属性一般遍历children来获取。
Swift反射的使用样例
转换对象为字典
struct Person {var name: String = "张三"var isMale: Bool = truevar birthday: Date = Date()
}class Animal: NSObject {private var eat: String = "吃什么"var age: Int = 0var optionValue: String?
}class Cat: Animal {var like: [String] = ["mouse", "fish"]var master = Person()
}
遍历出字典
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)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 objc = Cat()
let mirror = Mirror(reflecting: objc)
let mirrorDic = mapDic(mirror: mirror)
print(mirrorDic)//打印结果
["like": ["mouse", "fish"], "optionValue": nil, "eat": "吃什么", "age": 0, "master": __lldb_expr_48.Person(name: "张三", isMale: true, birthday: 2020-01-02 11:24:30 +0000)]
在实际运用中,可以将应用于元组参数传递(比如网路请求传参,传入元组,网络请求时转换为字典),优点:外部使用知道具体传入什么参数,参数更改不影响方法错误。
// 外部参数定义
var netParams = (title: "标题", comment: "评论,五星好评")// 网络层统一转换为字典,进行网路请求
let parmsDic = mapDic(mirror: Mirror(reflecting: netParams))
print(parmsDic)// 打印结果
["title": "标题", "comment": "评论,五星好评"]
但是需要注意,只能传入基本类型。且元组参数要命名,如果直接使用("标题","评论,五星好评")则会变成下面这种情况。
// 外部参数定义
var netParams = ("标题","评论,五星好评")//(title: "标题", comment: "评论,五星好评")// 网络层统一转换为字典,进行网路请求
let parmsDic = mapDic(mirror: Mirror(reflecting: netParams))
print(parmsDic)
// 打印
[".1": "评论,五星好评", ".0": "标题"]
获取类型,属性个数及其值
1
2
3
4
5
6
7
|
//用户类
class User {
var name: String = "" //姓名
var nickname: String ? //昵称
var age: Int ? //年龄
var emails:[ String ]? //邮件地址
}
|
接着创建一个用户对象,并通过反射获取这个对象的信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//创建一个User实例对象
let user1 = User ()
user1.name = "hangge"
user1.age = 100
user1.emails = [ "hangge@hangge.com" , "system@hangge.com" ]
//将user对象进行反射
let hMirror = Mirror (reflecting: user1)
print ( "对象类型:\(hMirror.subjectType)" )
print ( "对象子元素个数:\(hMirror.children.count)" )
print ( "--- 对象子元素的属性名和属性值分别如下 ---" )
for case let (label?, value) in hMirror.children {
print ( "属性:\(label) 值:\(value)" )
}
|
控制台输出信息如下:
通过属性名(字符串)获取对应的属性值,并对值做类型判断(包括是否为空)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//根据属性名字符串获取属性值
func getValueByKey(obj: AnyObject , key: String ) -> Any {
let hMirror = Mirror (reflecting: obj)
for case let (label?, value) in hMirror.children {
if label == key {
return unwrap(value)
}
}
return NSNull ()
}
//将可选类型(Optional)拆包
func unwrap(any: Any ) -> Any {
let mi = Mirror (reflecting: any)
if mi.displayStyle != . Optional {
return any
}
if mi.children.count == 0 { return any }
let (_, some) = mi.children.first!
return some
}
|
下面是实际测试样例,同样用上例的User对象做测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
//创建一个User实例对象
let user1 = User ()
user1.name = "hangge"
user1.age = 100
user1.emails = [ "hangge@hangge.com" , "system@hangge.com" ]
//通过属性名字符串获取对应的值
let name = getValueByKey(user1, key: "name" )
let nickname = getValueByKey(user1, key: "nickname" )
let age = getValueByKey(user1, key: "age" )
let emails = getValueByKey(user1, key: "emails" )
let tel = getValueByKey(user1, key: "tel" )
print (name, nickname, age, emails, tel)
//当然对于获取到的值也可以进行类型判断
if name is NSNull {
print ( "name这个属性不存在" )
} else if (name as ? AnyObject ) == nil {
print ( "name这个属性是个可选类型,且为nil" )
} else if name is String {
print ( "name这个属性String类型,其值为:\(name)" )
}
if nickname is NSNull {
print ( "nickname这个属性不存在" )
} else if (nickname as ? AnyObject ) == nil {
print ( "nickname这个属性是个可选类型,且为nil" )
} else if nickname is String {
print ( "nickname这个属性String类型,其值为:\(nickname)" )
}
if tel is NSNull {
print ( "tel这个属性不存在" )
} else if (tel as ? AnyObject ) == nil {
print ( "tel这个属性是个可选类型,且为nil" )
} else if tel is String {
print ( "tel这个属性String类型,其值为:\(tel)" )
}
|
控制台输出信息如下:
通过KVC访问属性值
(注意:正由于KVC是基于Objective-C的,所以其不支持可选类型(optional)的属性,比如上例的 var age:Int?
因此用户类做如下改造:)
1
2
3
4
5
6
7
|
//用户类
class User : NSObject {
var name: String = "" //姓名
var nickname: String ? //昵称
var age: Int = 0 //年龄
var emails:[ String ]? //邮件地址
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
//创建一个User实例对象
let user1 = User ()
user1.name = "hangge"
user1.age = 100
user1.emails = [ "hangge@hangge.com" , "system@hangge.com" ]
//使用KVC取值
let name = user1.valueForKey( "name" )
let nickname = user1.valueForKey( "nickname" )
let age = user1.valueForKey( "age" )
let emails = user1.valueForKey( "emails" )
//let tel = user1.valueForKey("tel")
print (name, nickname, age, emails)
//当然对于获取到的值也可以进行类型判断
if name == nil {
print ( "name这个属性是个可选类型,且为nil" )
} else if name is String {
print ( "name这个属性String类型,其值为:\(name)" )
}
if nickname == nil {
print ( "nickname这个属性是个可选类型,且为nil" )
} else if nickname is String {
print ( "nickname这个属性String类型,其值为:\(nickname)" )
}
|
1
2
3
4
5
6
7
8
9
|
//创建一个User实例对象
let user1 = User ()
//使用KVC赋值
user1.setValue( "hangge" , forKey: "name" )
user1.setValue(100, forKey: "age" )
user1.setValue([ "hangge@hangge.com" , "system@hangge.com" ], forKey: "emails" )
print (user1.name, user1.nickname, user1.age, user1.emails)
|
Swift 反射Mirror的使用相关推荐
- Swift之深入解析反射Mirror与错误处理
一.反射 Mirror 简介 反射是指可以动态获取类型.成员信息,同时在运行时(而非编译时)可以动态调用任意方法.属性等行为的特性. 在使用 OC 开发时很少强调其反射概念,因为 OC 的 runti ...
- 反射 Mirror | Swift 动态性
Mirror是Swift中的反射机制,反射就是可以动态的获取类型以及成员信息,同时也可以在运行时动态的调用方法和属性等. 1. Mirror 简介 Mirror是Swift中的反射机制的实现,它的本质 ...
- JAVA中的isMirror函数_Swift中的反射Mirror
Swift中的反射Mirror [TOC] 前言 Mirror是Swift中的反射机制,对于C#和Java开发人员来说,应该很熟悉反射这个概念.反射就是可以动态的获取类型以及成员信息,同时也可以在运行 ...
- Swift之深入解析反射Mirror的底层原理
一.Mirror 的底层分析 ① 反射 API 反射 API 由两部分实现: 一部分是通过 Swift 实现,即 ReflectionMirror.swift: 一部分是通过 C++ 实现,即 Ref ...
- Swift反射API及其用法
尽管 Swift 一直在强调强类型.编译时安全和静态调度,但它的标准库仍然提供了反射机制.可能你已经在很多博客文章或者类似Tuples.Midi Packets和Core Data的项目中见过它.也许 ...
- Swift 反射 API 及用法
作者:Benedikt Terhechte,原文链接,原文日期:2015-10-24 译者:mmoaay:校对:千叶知风:定稿:千叶知风 尽管 Swift 一直在强调强类型.编译时安全和静态调度,但它 ...
- swift之字典转模型kvc、mjextention桥接、反射、HandyJSON、ObjectMapper、Codable
参考swift4.0字典转模型:https://www.cnblogs.com/shaoting/p/8087153.html =====================kvc字典转模型======= ...
- HandyJSON:Swift语言JSON转Model工具库
背景 JSON是移动端开发常用的应用层数据交换协议.最常见的场景便是,客户端向服务端发起网络请求,服务端返回JSON文本,然后客户端解析这个JSON文本,再把对应数据展现到页面上. 但在编程的时候,处 ...
- swift enum高级用法
本文是一篇详细且具有实战意义的教程,涵盖几乎所有枚举(Enum)知识点,为你解答Swift中枚举的应用场合以及使用方法. 和switch语句类似,Swift中的枚举乍看之下更像是C语言中枚举的进阶版本 ...
最新文章
- 疫情之下的网站优化该怎样进行?
- hive使用derby的服务模式(可以远程模式)
- 构造、拷贝构造、赋值、析构
- 阿里云上JDK安全证书的实际位置
- mysql 5.5 重新编译_源码编译mysql5.5过程记录
- Windows XP和Windows 7双系统安装和启动菜单修复
- Hive数据如何同步到MaxCompute之实践讲解
- apache目录 vscode_VsCode搭建Java开发环境(Spring Boot项目创建、运行、调试)
- 视频PPT互动问答丨Oracle Groundbreak亚太巡演2021(中国区)
- Google Go 初识
- BOOST库介绍(二)——BOOST多线程相关库
- APMServ5.2.6win10系统Apache、MySQL5.1启动失败解决办法
- 关于Big-Endian 和Little-Endian
- pycharm2020版本以上中文版教程
- C语言的主要用途以及前景开展
- 【读书笔记】心理学与生活
- 【OpenGrok代码搜索引擎】二、Windows10下基于Linux子系统搭建Opengrok代码搜索引擎
- offsetX,offsetLeft,offsetWidth的区别详解
- python与工程造价的联系_工程造价和工程预算是一个概念吗?
- 云主机装黑果实践(4):阿里轻量机上变色龙bootloader启动问题