Swift中的类型和元类型

原文链接: Types and meta types in swift

在siwft中类型和元类型并不显得像Objc那样重要. 在Objc中不需要类型名也能实例化一个对象或者在运行中修改类,然而在纯siwft中这些都是不允许的.

事实上siwft是一个支持泛型的语言, 并且几乎没有反射能力(reflection capabilities), 获取和处理类型和元类型在swift中并不常见.

然而, 有时候我们必须或者很想知道一个实例(或者类)的类型, swift提供了两种方法获取类型; 用dynamicType获得实例的类型, 用self获取类或结构体的类型.

Type.self

对一个类型调用self方法可以获取到该类型的元类型. 返回值可能是一个Class metatype, Struct metatype, 或者是Protocol metatype.



尽管上面的例子看起来都很相似, 然而实际上并不是. NSString.self返回的值看起来很像Objective-C类. 实际上它(NSString.self 返回的类)还包含了一些用seift扩展定义的其他方法, 例如调用NSString.self.className()会得到“NSString”.

调用MyClass.self得到的对象其实就是MyClass的swift元类型(强调swift以区分objc中的元类概念). 定义在swift对象离的init方法和所有定在这个类中的所有curried method都会对外暴露出来(Instance Methods are Curried Functions in Swift)

如果我们把MyClass.self 传到NSStringFromClass方法中,会得到一个奇怪的类名. 

String.self调用返回值似乎没什么问题. 有一点小小的区别值得提一提,如果把String.self传到NSStringFromClass会得到一个编译错误. 不要太吃惊, String是一个结构体不是一个类.

dynamicType

如果对Type调用.self会得到它的metatype, 获取一个实例的Type可以dynamicType方法. 对一个类调用.self和实例对象调用dynamicType的返回结果是一样的都是metatype. 

然而, 这个规则对String, Int和Double无效.



作者认为, 这是因为String, Int和Double在swift中是特殊类型. 实际上swift能无缝的转换成Foundation类型,直接对其元类型进行比较是不可能的.

更新: Joe Groff, swift team成员之一在twitter上透露,这个或许是一个bug. Screen Shot 2016-03-23 at 10.59.01 A

所以 dynamicType和Type.self进行比较成为可能: 

类型比较并不是一个主意最好不要那么做. 检查一个swift的类型的一个安全方式是使用swift的is语法. 如下面比较Double的方式(请忽略警告) 

我们可以用Type实例化一个对象. 比如下面我们用 MyClass的类型实例化一个对象: 

用 dynamicType初始化却不能: 

通过错误提示描述,给MyClass添加required init方法: 

Holding a metatype references

当用一个变量存”“.dynamicType或者String.self的返回值,其引用的类型为String.Type.String.Type通常用于声明元类型变量: 

用String.Type或者任何Struct.Type声明的变量且仅可以存结构体的meta-type.编译器不允许对同一个变量赋于不同的meta-type: 


对于类(区别于结构体), 因为其能子类化, 其行为不同于上面的例子. 一个BaseClass.Type能引用SubClass.Type.

下面我们用NSDictionary.self和NSMutableDictionary.self给同一个变量赋值: 

值得注意的事上面的例子反过来不成立,如果声明的是NSMutableDictionary.Type类型变量,编译器不允许引用NSDictionary.self.


毫无疑问, Protocol.Type变量能引用所有实现了其协议的类型: 

这里要注意,没有关联type或者Self的协议不能用于声明变量.有关联type或者Self协议也仅仅用于泛型约束上.



最后要注意的是, 声明为 Any.Type的变量可以引用任何类型, 无论是类还是结构体. 

AnyObject.Type在另一方面, 可以引用任何类的元类型.