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