type(of: ) 获取参数类型的方法
-
下面的代码中咱们声明了一个类ClassA,然后用ClassA创建了两个实例a1、a2,咱们用type(of: )方法打印这两个实例的类型,结果打印结果都是ClassA。
因为a1、a2是用ClassA创建出来的,所以a1、a2 是ClassA类型这个结果是符合咱们预期的。
-
但是a1和a2创建时,有点小差别,就是a1没有声明类型,a2明确的声明了是ClassA类型,咱们知道swift语言中,没声明的话,会自动根据后面的值确定类型,所以a1会被默认声明成ClassA类型
class ClassA { var value = 10 } let a1 = ClassA() let a2: ClassA = ClassA() print(type(of: a1)) print(type(of: a2)) 打印结果 ClassA ClassA
-
咱们再看一下官方是怎么介绍
type(of:)
的:You can use thetype(of:)
function to find the dynamic type of a value, particularly when the dynamic type is different from the static type.就是获取传入参数的动态类型,尤其是传入参数的静态类型和动态不一致时。-
什么是静态类型,什么是动态类型
静态类型可以简单的理解为,你从外观可以看出来的类型,也就是这个变量声明时的类型,就和上面let a2: ClassA说自己是
中a2说自己是ClassA类型,动态类型就是这个变量实际是哪个类型的,比如上面
let a1 = ClassA()
=号后面是啥类型,他就是啥类型 -
举个例子
class ClassA { var value = 10 } class ClassB: ClassA { var value2 = 20 } let a: ClassA = ClassB() print(type(of: a)) 打印结果 ClassB
从这行代码可以看出
let a: ClassA = ClassB()
a的静态类型是ClassA(也就是声明时给自己定的类型),但是a的动态类型是ClassB,也就是实际的类型。就类似一个瓶子上贴着茅台酒的标签,里装的是北京二锅头。但是type(of:)
就是获取动态(真实)类型的
-
-
type(of: )获得的类型是等价于直接写的类型
- 比如下面,type(of: a) 和 ClassA是一样的
class ClassA { var value = 10 class func printName() { print("----ClassA") } } let a: ClassA = ClassA() type(of: a).printName() ClassA.printName() 打印结果 ----ClassA ----ClassA
- 官方的例子是这个
class Smiley { class var text: String { return ":)" } } class EmojiSmiley: Smiley { override class var text: String { return "😀" } } func printSmileyInfo(_ value: Smiley) { let smileyType = type(of: value) print("Smile!", smileyType.text) } let emojiSmiley = EmojiSmiley() printSmileyInfo(emojiSmiley) // Smile! 😀
- 注意的点
type(of:)
不仅可以用在像是class, structure, enumeration类、结构体、枚举等这种具体类型,也可以用于protocol协议这种有点虚拟的类型,例如下面(咱们可以先忽略 .self和.Protocol 后面会讲到,这里只是说明,参数可以是协议类型)
但是有个比较特殊的情况是:一个实例故意把自己说成是协议类型(也就是静态类型是协议),然后将这个参数通过一个泛型函数传进去,protocol protocolA { } print(type(of: protocolA.self)) 打印结果 protocolA.Protocol
type(of:)
会出来和预期不一样的结果,感兴趣的可以看看官方说明
.Type 是啥
1.说这些东西之前咱们先看一个生活中的例子,大家都见过这玩意吧, 00后的就不确定了 ~ ,90、80后应该都见过鱼模具,过年时,拿出这个模具里面放上面团做几个好看一点的鱼
那这些模具是怎么来的呢,有可能是3D打印机打印的
2.看到上面这个例子,咱们应该清楚
鱼是通过鱼的模具做出来的,鱼的模具是用3D打印机做的,3D打印机是别的机器造出来的等等。一个东西都有它的由来,不是凭空出现的
-
同理咱们再用上面的
ClassA
举例,a1是用ClassA初始化来的,就好比鱼是用鱼模具做出来的,那ClassA是怎么来的呢(鱼模具是怎么来的呢),咱们先用type(of:)
打印一下看看,看下面的代码,得知ClassA是ClassA.Type类型,也就是ClassA是由ClassA.Type创造的,因为没有具体的名称表示,所以就直接在ClassA后面加上个Type吧。那问题来了ClassA.Type是由啥创造出来的呢,很明显是ClassA.Type.Type,可以自己打印看看结果(注意下面的.self可以忽略,可以以当做没写.self。后面会说明.self是干啥的)class ClassA { var value = 10 } print(type(of: ClassA.self)) 打印结果 ClassA.Type
-
所以说x.Type 是指x的类型,通俗点就是指:是谁创造了x 。咱们举个例子,继续用ClassA举🌰,看下面的代码,这种方法咱们经常见到
testType1(value: ClassA)
传入的参数类型是ClassA。也就是说value的类型是ClassA,谁的类型是ClassA,a1就是,所以testType1(value: a1)
咱们再看testType2(value: ClassA.Type)
这个方法要求传入的参数类型是ClassA.Type
,谁的类型是呢,那就是ClassA的类型是ClassA.Type,所以testType2(value: ClassA.self)
class ClassA { var value = 10 func test1() { } class func test2() { } } func testType1(value: ClassA) { } func testType2(value: ClassA.Type) { } let a1 = ClassA() testType1(value: a1) testType2(value: ClassA.self)
咱们可以理解为,判断一个参数的类型,就是判断这个参数是由啥模板创建出来的。"Hello, Type"是由String模板创建出来的,咱们用类举一个例子,ClassA就是咱们声明了一个类(模板),用ClassA这个模板创建了一个a,所以a的类型就是ClassA(创建a的模板)
首先定义一个字符串var str = "Hello, type"
当我问str是什么类型时,咱们都会说是String类型,这就是type(of: )的作用:告诉咱们传入的参数属于啥类型,如下面的例子
var str = "Hello, Type"
print(type(of: str))
print(type(of: "Hello, Type"))
打印结果:
String
String
怎么理解呢,就是咱们声明了一个类ClassA(就类似上面说的鱼模具),然后创建了两个实例a1、a2(类似上面说的鱼),type(of: )方法就是获取传入的参数是由哪个模具创造出来的
AnyObject
官方是怎么介绍的呢:can be used as the concrete type for an
instance of any class, class type, or class-only protocol
可以用来表示类实例,类,和类专属的协议。也就是只和类相关的,有的文章说用于表示类实例的,这种说法不严谨,后面会说到
1. 表示 "实例类型"
-
比如用AnyObject来表示Swift的实例类型。实例的类型是啥,换种说法就是a1的类型(ClassA),说白了就是代表 "类",如下面的代码,AnyObject就是代替了ClassA
class ClassA { } let aaa: AnyObject = ClassA()
-
还可以表示OC中的实例类型,因为在OC中NSString、NSNumber都是类
let s: AnyObject = "This is a bridged string." as NSString let v: AnyObject = 100 as NSNumber
-
当用到这些实例的时候,可以用as 来尝试转成对应的类型
if let message = s as? String { print("Successful cast to String: \(message)") } Prints "Successful cast to String: This is a bridged string."
2 可以调用所有用@objc修饰的方法,不管是不是自己的方法都可以调用
- 例如声明两个类 ClassA 、ClassB
class ClassA { @objc func printMessageA1() { print("A1-----") } @objc func printMessageA2() { print("A2-----") } } class ClassB { @objc func printMessageB1() { print("B1-----") } @objc func printMessageB2() { print("B2-----") } }
-
当我用AnyObject表示的实例进行方法调用时,会联想出所有带@objc的方法
- 需要咱们注意到联想出来的方法后面都是带有!的,说明调用的方法是可选类型,就是可以用?也可以用! 。但是默认是!强制解析的。当咱们不确定这个方法是不是属于自己时,建议用?否则会崩溃
3 AnyObject不仅可以表示实例类型也可以代表类的类型(不常用)
- 定义ClassA,声明一个类方法,只有类可以调用
class ClassA {
@objc class func printMessageA3() {
print("A3-----")
}
}
- 用AnyObject表示一个类的类型,然后调用类方法
let calss: AnyObject = ClassA.self
(calss as? ClassA.Type)?.printMessageA3()
打印结果
A3-----
AnyClass
文档中是这么定义的
public typealias AnyClass = AnyObject.Type
也就是一般用于表示类的类型-
怎么用呢,咱们还是用之前的类ClassA为例
class ClassA { class func printMessageA3() { print("A3-----") } } let anyClass: AnyClass = ClassA.self (anyClass as? ClassA.Type)?.printMessageA3() 打印结果 A3-----
-
AnyClass类型的变量可以调用所有用@objc声明的类方法,举个🌰,声明两个类ClassA、ClassB。分别有两个类方法printMessageA3、printMessageB3。然后定义一个anyClass,就会看到这个anyClass可以调用所有带有@objc的类方法。当然还是会默认强制解包,这个咱们需要注意,不确定的用?比较好
class ClassA { @objc class func printMessageA3() { print("A3-----") } } class ClassB { @objc class func printMessageB3() { print("B3-----") } } let anyClass: AnyClass = ClassA.self anyClass.printMessageA3() anyClass.printMessageB3?()
.self
- .self用在实例后面代表这个实例本身。两种调用printMessageA1()方式,打印的结果是一样的,只是平时可以省略不需要写
class ClassA { func printMessageA1() { print("A1-----") } } let a: ClassA = ClassA() a.printMessageA1() a.self.printMessageA1()
- 用在类型后面代表类型本身,也就是变量代表的类型
ClassA.self就是代表ClassA类型,一般用在函数的参数是类型时。比如需要传一个参数,参数的类型是ClassA.Type,什么参数的类型是ClassA.Type,那就是ClassA类型("类型"、"的类型"表述的东西是不一样的)class ClassA { func testType(value: ClassA.Type) { } } let a: ClassA = ClassA() a.testType(value: ClassA.self)
比如tableview的注册cell的方法,参数类型是AnyClass,谁的类型是AnyClass ,那就是UITableViewCell类型
func register(AnyClass?, forCellReuseIdentifier: String)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
总结
以ClassA、Int为例,则他们的等级就是如下,可以和最开始说的模具联系起来
class ClassA {
}
let a = ClassA()
一级 | 二级 | 三级 | 四级 |
---|---|---|---|
a | ClassA | ClassA.Type | ClassA.Type.Type |
5 | Int | Int.Type | Int.Type.Type |
AnyObject在这级 | AnyClass在这级 |