Swift 反射

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift 反射相关的知识,希望对你有一定的参考价值。

参考技术A Swift作为一门静态语言,所有资料的型别都是在编译时就确定好了的,但是Apple为了让Swift相容Objc,让Swift也使用了Runtime。这显然会拖累Swift的执行效率,和Apple所宣称Swift具有超越Objective-C的效能的观点完全不符。而Swift在将来是会慢慢替代 Objective-C的成为iOS或者OSX开发的主流语言,所以为了效能,我们应该尽量使用原生的Swift,避免让Runtime进行Swift型别->Objc型别的隐式转换。
Swift目前只有有限的反射功能,完全不能和Objc的Runtime相比。

反射是一种计算机处理方式。是程式可以访问、检测和修改它本身状态或行为的一种能力。
上面的话来自百度百科。使用反射有什么用,看一些iOS Runtime的文章应该会很明白。下面再列举一下

上面的一系列功能的细节和计算机语言的不同而不同。对于Objective-C来说,位于中的一系列方法就是完成这些功能的,严格来说Runtime并不是反射。而Swift真正拥有了反射功能,但是功能非常弱,目前只能访问和检测它本身,还不能修改。

Swift的反射机制是基于一个叫Mirror的Stuct来实现的。具体的操作方式为:首先建立一个你想要反射的类的例项,再传给Mirror的构造器来例项化一个Mirror物件,最后使用这个Mirror来获取你想要的东西。

下面来简单介绍下Mirror的使用:

Swift - 反射(Reflection)的介绍与使用样例(附KVC介绍)

1,反射(Reflection)

对于C#、Java开发人员来说,肯定都对反射这个概念相当熟悉。所谓反射就是可以动态获取类型、成员信息,同时在运行时(而非编译时)可以动态调用任意方法、属性等行为的特性。

以Java上的两个知名框架(hibernate和spring)为例。hibernate的属性映射就是通过反射来赋值的,spring的bean的创建就是根据配置的class来反射构建的。
 
2,Objective-C 的 Runtime
在使用ObjC开发时很少强调其反射概念,因为ObjC的Runtime要比其他语言中的反射强大的多。在ObjC中可以很简单的实现字符串和类型的转换(NSClassFromString()),实现动态方法调用(performSelector: withObject:),动态赋值(KVC)等等。
 
3,Swift中的反射
在Swift中并不提倡使用Runtime,而是像其他语言一样使用反射(Reflect)。当然,目前Swift中的反射还没有其他语言中的反射功能强大,不仅远不及OC的Runtime,离Java的反射也有一定的距离。
Swift的反射机制是基于一个叫 Mirror 的 struct 来实现的,其内部有如下属性和方法:
1
2
3
4
let children: Children   //对象的子节点。
displayStyle: Mirror.DisplayStyle?   //对象的展示风格
let subjectType: Any.Type   //对象的类型
func superclassMirror() -> Mirror?   //对象父类的 mirror

4,Swift反射的使用样例

样例1:输出实体对象的类名,属性个数,以及所有属性的属性名和属性值。
首先定义一个用户类:
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
         
//将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)")
}
控制台输出信息如下:  
技术分享图片
 
 
样例2:通过属性名(字符串)获取对应的属性值,并对值做类型判断(包括是否为空)
首先为方便使用,这里定义两个方法。getValueByKey()是用来根据传入的属性名字符串来获取对象中对应的属性值。unwrap()是用来给可选类型拆包的(对于非可选类型则返回原值)
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
 
//通过属性名字符串获取对应的值
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 asAnyObject) == nil {
    print("name这个属性是个可选类型,且为nil")
}else if name is String {
    print("name这个属性String类型,其值为:\(name)")
}
 
if nickname is NSNull {
    print("nickname这个属性不存在")
}else if (nickname asAnyObject) == nil {
    print("nickname这个属性是个可选类型,且为nil")
}else if nickname is String {
    print("nickname这个属性String类型,其值为:\(nickname)")
}
 
if tel is NSNull {
    print("tel这个属性不存在")
}else if (tel asAnyObject) == nil {
    print("tel这个属性是个可选类型,且为nil")
}else if tel is String {
    print("tel这个属性String类型,其值为:\(tel)")
}
控制台输出信息如下:
技术分享图片
 
 
附:通过KVC访问属性值
KVC是key-value coding的缩写。它是一种间接访问对象的机制。其本质是依据OC中Runtime的强大动态能力来实现的。在Swift中,只要类继承NSObject即可使用KVC。(有一个叫KVO的,它又是基于KVC,大家有兴趣的可以自行研究下。)
KVC中:key的值就是属性名称的字符串,返回的value是任意类型,需要自己转化为需要的类型。
(注意:正由于KVC是基于Objective-C的,所以其不支持可选类型(optional)的属性,比如上例的 var age:Int? 
因此用户类做如下改造:)
1
2
3
4
5
6
7
//用户类
class UserNSObject{
    var name:String ""  //姓名
    var nickname:String?  //昵称
    var age:Int = 0  //年龄
    var emails:[String]?  //邮件地址
}
KVC主要就是两个方法:
(1)通过key获得对应的属性值
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
 
//使用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)")
}
(2)通过key设置对应的属性值
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(["[email protected]","[email protected]"], forKey: "emails")
         
print(user1.name, user1.nickname, user1.age, user1.emails)


原文出自:www.hangge.com  转载请保留原文链接:http://www.hangge.com/blog/cache/detail_976.html

 

http://www.hangge.com/blog/cache/detail_976.html










以上是关于Swift 反射的主要内容,如果未能解决你的问题,请参考以下文章

Swift 反射

Swift - 反射(Reflection)的介绍与使用样例(附KVC介绍)

Swift之深入解析反射Mirror的底层原理

Swift之深入解析反射Mirror与错误处理

反射 Swift 2.0 XCode 7 beta 5

iOS开发-Swift进阶之反射Mirror & 错误处理!