任意类型、泛型、强制类型转换在开发中也是经常用到。
一、Any、AnyObject
Swift提供了2种特殊的类型:Any
,AnyObject
。
Any: 可以代表任意类型(枚举、结构体、类,也包括函数类型)
AnyObject: 可以代表任意类类型(在协议后面写上:AnyObject
代表只有类能遵守这个协议)
示例代码一:
1 | class Person { } |
如果变量p
后面是具体类型或不写类型就不能编译通过。
示例代码二(存放任意类型的数组):
1 | var data = Array<Any>() |
示例代码三:
1 | protocol Runnable: AnyObject { } |
如果上面的示例代码三使用
struct
:改用
Any
即可正常使用:1
2
3
4protocol Runnable: Any { }
struct Person: Runnable {
}
二、is、as?、as!、as
is
用来判断是否为某种类型,as
用来做强制类型转换。
示例代码一(is
的使用):
1 | protocol Runnable { |
示例代码二(as
的使用):
1 | (stu as? Student)?.study() // 没有输出 |
as?
:转换为可选类型as!
:强制转换类型(失败后会报错)as
:一定能够强制转换成功的时候使用。
as
的应用场景一:
1 | var data = [Any]() |
as
的应用场景二:
1 | var d = 10 as Double |
三、X.self、X.Type、AnyClass
X.self
是一个元类型(metadata
)的指针,metadata
存放着类型相关信息。AnyClass
是AnyObject.Type
类型,表示任意元类型。
示例代码一:X.self
属于X.Type
类型。Person.self
类似于OC中的Person.class
。
1 | class Person { |
汇编分析:
Person.self
代表的是指针p
指向的实例对象在堆空间存放的前8个字节(元类型信息地址)。
示例代码二:
1 | class Person { } |
上面的代码简单描述就是:父类指针可以指向子类实例。
示例代码三:
1 | var per = Person() |
type(of: per)
本质就是把对象的前8个字节取出来。
四、元类型的应用
示例代码一:
1 | class Animal { |
Animal()
和Animal.self.init()
效果一致。类似OC中[[Animal.class alloc] init]
。
上面代码中基类
Animal
的初始化方法加了required
,为什么呢?
因为子类初始化器必须实现父类的初始化器,否则有可能找不到init
方法导致程序崩溃。
示例代码二:
Swift支持部分runtime
函数。
1 | class Person { |
从结果可以看出来,Swift还有个隐藏的基类:Swift.SwiftObject
可以参考Swift源码:https://github.com/apple/swift/blob/master/stdlib/public/runtime/SwiftObject.h
五、Self
Self
代表当前类型。
示例代码:1
2
3
4
5
6
7
8
9
10class Person {
var age = 1
static var count = 2
func run() {
print(self.age) // 输出:1
print(Self.count) // 输出:2
}
}
var p = Person()
p.run()Self
一般用作返回值类型(也可以作为参数类型),限定返回值跟方法调用者必须是同一类型。类似于OC中的instancetype
。
示例代码:
1 | protocol Runnable { |
结果:谁调用就返回谁的实例。