Any和AnyObject是swift中两个产物,很容易让人迷惑,在swift官方中:
写过Objective-C的读者都知道Objective-C有一个叫做id的神奇东西。
编译器不会对声明为id的变量进行类型检查,它可以表示任意类型的实例。
在Cocoa框架中很对地方都使用了id来进行像参数传递和方法返回这样的工作,这是Objectice-C动态特性的一种表现。现在Swift最主要的用途是使用Cocoa框架进行app开发,因此为了与Cocoa框架协作,使用了一种类似的,可以代表任意class类型的AnyObject来代替原来id的概念。
这个虽然符合Objective-C中的理念,但是在Swift环境下使用起来就非常麻烦,也很危险。应该选择的做法是在使用时先确定AnyObject真正的类型并进行转换后在进行调用。
假设原来的API返回的是一个id,那么在Swift中被映射为AnyObject?(因为id是可以指向nil的,所以在这里我们需要一个Optional的版本),虽然调用应该是没问题的,但是我们最好这样写:
func someMethod() -> AnyObject?{
// ...
//返回一个AnyObject?,等价于在Objective-C中返回一个id
return result
}
let anyObject: AnyObject?= SomeClass.someMethod
if let someInstance = anyObject as? SomeRealClass {
// 拿到具体的 SomeRealClass的实例
someInstance.funcOfSomeRealClass()
}
AnyObject 的定义,它其实就是一个接口:
protocol AnyObject {
}
所有的class都隐式的实现了这个接口,这也是AnyObject只适用于class的原因。而在swift 中所有的基本数据类型(Int、Double、Float、String、Array、和 Dictionary),全部都是struct类型,因此苹果又提出的Any
在Swift中能表示“任意”这个概念的除了Any和AnyObject以外还有AnyClass 。AnyClass 在Swift 中被一个typealias所定义:
typealias AnyClass = AnyObject.Type
通过AnyObject.Type这种方式所的到的是一个元类型(Meta)。在声明时我们总是在类型的名称后面加上.type,比如A.Type 代表的是A类型的类型。也就是说,我们可以声明一个元类型来存储A这个类型本身,从而A中取出其类型时,我们需要用到.self:
class A {
}
let typeA: A.Type = A.self
在swift中,.self的作用
在了解这个基础之后,我们就明白AnyObject.type ,或者说AnyClass所表达的东西其实并没有什么奇怪的,它就是任意类型的本身。所以,对于上面A的类型的取值,我们也可以强制让它是一个AnyClass:
class A {
}
let typeA: AnyClass = A.self
这样,要是A中有一个类方法,我们就可以通过 typeA来调用了:
class A {
class func method() {
print("Hello")
}
}
let typeA: A.Type = A.self
typeA.method()
// 或者
let anyClass:AnyClass = A.self
(anyClass as A.Type).method()
这样做得意义,难道不是直接使用A.method()来调用吗??没错对于独立的类型来说完全没有必要关心元类型,但是元类型或者元编程的概念可以变得非常灵活和强大,在这我们编写某些框架性的代码会非常方便。
比如我们想要传递一些类型的时候,就不需要不断地去改动代码了
下面的例子中虽然我们是用代码声明的方式获取了MusicViewController 和AlbumViewController的元类型,但是这一步完全可以通过读入配置文件之类的方式来完成。而在将这些元类型存入数组并且传给别的方法来配置这一点上,元类型编程就很难被替代了:
class MusicViewController: UIViewController {
}
class AlbumViewController: UIViewController {
}
let userimgVCTypes: [AnyClass] = [MusicViewController.self,
AlbumViewController.self]
func setupViewControllers(_ vcTypes: [AnyClass]){
for vcType in vcTypes {
if vcType is UIViewController.Type {
let vcClass = vcType as! UIViewController.Type // 获取类
let vc = vcClass.init() // 获取类实例对象
print(vc)
}
}
}
setupViewControllers(userimgVCTypes)
这么一来,我们完全可以搭好框架,然后用DSL的方式进行配置,就可以在不触及swift编码的情况下,很简单地完成一系列复杂的操作了。
另外在Cocoa AP 中我们也经常遇到需要输入一个AnyClass 的情况,这时候我们也应该使用 .self 的方式来获取所需的元类型,例如在tableView的cell的类型的时候:
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellId")
.Type 表示的是某个类型的元类型,而在swift中,除了class,struct和enum这三个类型,我们还可以定义protocol.对于protocol来说,有时候我们也会想取得接口的元类型。这时我们可以在某个protocol的名字后面使用.protocol来获取,使用的方法和.Type类似的。