【iOS】Swift系列十七 - 任意类型

任意类型、泛型、强制类型转换在开发中也是经常用到。

一、Any、AnyObject

Swift提供了2种特殊的类型:AnyAnyObject

Any: 可以代表任意类型(枚举、结构体、类,也包括函数类型)

AnyObject: 可以代表任意类型(在协议后面写上:AnyObject代表只有类能遵守这个协议)

示例代码一:

1
2
3
4
class Person { }
var p: Any = 10
p = "idbeny"
p = Person()

如果变量p后面是具体类型或不写类型就不能编译通过。

示例代码二(存放任意类型的数组):

1
2
3
4
5
6
7
8
var data = Array<Any>()
// 数组的另外一种写法
// var data = [Any]()
data.append(1)
data.append(1.01)
data.append(Person())
data.append("idbeny")
data.append({ 10 })

示例代码三:

1
2
3
4
protocol Runnable: AnyObject { }
class Person: Runnable {

}
  • 如果上面的示例代码三使用struct

  • 改用Any即可正常使用:

    1
    2
    3
    4
    protocol Runnable: Any { }
    struct Person: Runnable {

    }

二、is、as?、as!、as

is用来判断是否为某种类型,as用来做强制类型转换。

示例代码一(is的使用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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的使用):

1
2
3
4
5
6
(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的应用场景一:

1
2
var data = [Any]()
data.append(Int("123") as Any)

as的应用场景二:

1
2
var d = 10 as Double
print(d) // 输出:10.0

三、X.self、X.Type、AnyClass

X.self是一个元类型(metadata)的指针,metadata存放着类型相关信息。AnyClassAnyObject.Type类型,表示任意元类型。

示例代码一:
X.self属于X.Type类型。Person.self类似于OC中的Person.class

1
2
3
4
5
class Person {

}
var p = Person()
var pType: Person.Type = Person.self

汇编分析:

Person.self代表的是指针p指向的实例对象在堆空间存放的前8个字节(元类型信息地址)。

示例代码二:

1
2
3
4
5
6
7
8
9
10
11
12
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

上面的代码简单描述就是:父类指针可以指向子类实例。

示例代码三:

1
2
var per = Person()
print(Person.self == type(of: per)) // 输出:true

type(of: per)本质就是把对象的前8个字节取出来。

四、元类型的应用

示例代码一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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函数。

1
2
3
4
5
6
7
8
9
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

  1. Self代表当前类型。
    示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Person {
    var age = 1
    static var count = 2
    func run() {
    print(self.age) // 输出:1
    print(Self.count) // 输出:2
    }
    }
    var p = Person()
    p.run()
  2. Self一般用作返回值类型(也可以作为参数类型),限定返回值跟方法调用者必须是同一类型。类似于OC中的instancetype

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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()

结果:谁调用就返回谁的实例。