Swift中的反射

发布于:2014-06-23 15:59阅读数:

Swift 其实是支持反射的,不过功能略弱。本文介绍基本的反射用法和相关类型。

作者:andelf(个人博客CocoaChina主页
 
Swift 其实是支持反射的,不过功能略弱。本文介绍基本的反射用法和相关类型。
 
MetaType 和 Type 语法
The metatype of a class, structure, or enumeration type is the name of that type followed by .Type. The metatype of a protocol type—not the concrete type that conforms to the protocol at runtime—is the name of that protocol followed by .Protocol. For example, the metatype of the class type SomeClass is SomeClass.Type and the metatype of the protocol SomeProtocol is SomeProtocol.Protocol.
 
You can use the postfix self expression to access a type as a value. For example, SomeClass.self returns SomeClass itself, not an instance of SomeClass. And SomeProtocol.self returns SomeProtocol itself, not an instance of a type that conforms to SomeProtocol at runtime.
  • metatype-type –> type.Type | type.Protocol
  • type-as-value –> type.self
 
其中 metatype-type 出现在代码中需要类型的地方, type-as-value 出现在代码中需要值、变量的地方。
 
Any.Type 类型大家可以猜下它表示什么。
 
基础定义
 
反射信息用 Mirror 类型表示,类型协议是 Reflectable,但实际看起来 Reflectable 没有任何作用。
  1. protocol Reflectable { 
  2.   func getMirror() -> Mirror 
  3. protocol Mirror { 
  4.   var value: Any { get } 
  5.   var valueType: Any.Type { get } 
  6.   var objectIdentifier: ObjectIdentifier? { get } 
  7.   var count: Int { get } 
  8.   subscript (i: Int) -> (String, Mirror) { get } 
  9.   var summary: String { get } 
  10.   var quickLookObject: QuickLookObject? { get } 
  11.   var disposition: MirrorDisposition { get } 
 
实际上所有类型都实现了 Reflectable。
 
Mirror 协议相关字段:
  • 1.value 相当于变量的 as Any 操作
  • 2.valueType 获得变量类型
  • 3.objectIdentifier 相当于一个 UInt 作用未知,可能是 metadata 表用到
  • 4.count 子项目个数(可以是类、结构体的成员变量,也可以是字典,数组的数据)
  • 5.subscript(Int) 访问子项目, 和子项目的名字
  • 6.summary 相当于 description
  • 7.quickLookObject 是一个枚举,这个在 WWDC 有讲到,就是 Playground 代码右边栏的显示内容,比如常见类型,颜色,视图都可以
  • 8.disposition 表示变量类型的性质,基础类型 or 结构 or 类 or 枚举 or 索引对象 or … 如下
  1. enum MirrorDisposition { 
  2.   case Struct // 结构体 
  3.   case Class // 类 
  4.   case Enum // 枚举 
  5.   case Tuple // 元组 
  6.   case Aggregate // 基础类型 
  7.   case IndexContainer // 索引对象 
  8.   case KeyContainer // 键-值对象 
  9.   case MembershipContainer // 未知 
  10.   case Container // 未知 
  11.   case Optional // Type? 
  12.   var hashValue: Int { get } 
通过函数 func reflect<T>(x: T) -> Mirror 可以获得反射对象 Mirror 。它定义在 Any 上,所有类型均可用。
 
实际操作
 
.valueType 处理
Any.Type 是所有类型的元类型,所以 .valueType 属性表示类型。实际使用的时候还真是有点诡异:
  1. let mir = reflect(someVal) 
  2. swift mir.valueType { 
  3. case _ as String.Type: 
  4.     println("type = string"
  5. case _ as Range<Int>.Type: 
  6.     println("type = range of int"
  7. case _ as Dictionary<Int, Int>.Type: 
  8.     println("type = dict of int"
  9. case _ as Point.Type: 
  10.     println("type = a point struct"
  11. default
  12.     println("unkown type"
  13. }     
 
或者使用 is 判断:
  1. if mir is String.Type { 
  2.     println("!!!type => String"
is String 判断变量是否是 String 类型,而 is String.Type 这里用来判断类型是否是 String 类型。
 
subscript(Int) 处理
实测发现直接用 mir[0] 访问偶尔会出错,也许是 beta 的原因。
  1. for r in 0..mir.count { 
  2.     let (name, subref) = mir[r] 
  3.     prtln("name: \(name)"
  4.     // visit sub Mirror here 
通过上面的方法,基本上可以遍历大部分结构。
 
不同类型的处理
 
Struct 结构体、 Class 类
  • .count 为字段个数。
  • subscript(Int) 返回 (字段名,字段值反射 Mirror) 元组
  • summary 为 mangled name
  •  
Tuple 元组
  • .count 为元组子元素个数
  • subscript(Int) 的 name 为 “.0”, “.1” …
  •  
Aggregate 基础类型
包括数字、字符串(含 NSString)、函数、部分 Foundation 类型、 MetaType 。
 
很奇怪一点是测试发现枚举也被反射为基础类型。怀疑是没实现完。
  • .count 为 0
 
IndexContainer 索引对象
包括 Array<T>, T[], NSArray 等。可以通过 subscript 访问。
  • .count 为元组子元素个数
  • subscript(Int) 的 name 为 “[0]”, “[1]” …
 
KeyContainer 键-值对象
包括 Dictionary<T, U>、NSDictionary
  • .count 为元组子元素个数
  • subscript(Int) 的 name 为 “[0]”, “[1]” … 实际访问是 (name, (reflect(key), reflect(val)))
 
Optional Type?
只包括 Type?,不包括 Type!。
  • .count 为 0 或者 1 (对应 nil 和有值的情况)
  • subscript(Int) , name 为 “Some”
 
其他
Enum 枚举 看起来是未使用
MembershipContainer // 未知
Container // 未知
 
示例代码
 
 
zenny_chen补充(CocoaChina主页
 
我这里再补充几句。因为考虑到有些编程经验不是很丰富的朋友对于此问题可能会感觉比较迷糊。
楼主,贴了Apple官方Swift编程指南与编程手册第791页的第二段。其实第一段也比较重要——
 
A metatype type refers to the type of any type, including class types, structure types, enumeration types, and protocol types.
 
一个元类型是对任一类型(包括类类型,结构类型,枚举类型以及协议类型)的类型的引用。
 
在动态编程语言中,其复合类型(比如Swift中的类类型,结构类型,枚举类型以及协议类型)通常会以某种编码方式记录在运行时内存中,然后对外接口提供每种类型的唯一签名句柄。
 
举一个非常简单的例子。大家玩过Objective-C的话,[NSString class]所返回的Class句柄就是NSString的元类型。然后,我们通过runtime提供的各种接口就可以做很多事情。比如:
  1. Class strClass = [NSString class]; 
  2.     NSLog(@"The class name is: %s.", class_getName(strClass)); 
上述代码将输出The class name is NSString.
 
对于像Objective-C以及Swift这种比较动态的编程语言而言,元类型其实就是桥接我当前静态代码与运行时类型进行交互的玩意儿。通过元类型,我除了能够查询所对应的类型的详细信息之外,甚至还能对已存在的类型进行修改。比如修改成员属性、甚至替换成员方法的实现等。这种功能特性在计算机编程语言中也被称作为反射--Reflection。 
 
 

 

CocoaChina是全球最大的苹果开发中文社区,官方微信每日定时推送各种精彩的研发教程资源和工具,介绍app推广营销经验,最新企业招聘和外包信息,以及Cocos2d引擎、Cocos Studio开发工具包的最新动态及培训信息。关注微信可以第一时间了解最新产品和服务动态,微信在手,天下我有!

请搜索微信号“CocoaChina”关注我们!

搜索CocoaChina微信公众号:CocoaChina

顶部