type(of: )、.Type、AnyObject、AnyClass、.self的梳理

type(of: ) 获取参数类型的方法

  1. 下面的代码中咱们声明了一个类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
      
  2. 咱们再看一下官方是怎么介绍 type(of:) 的:You can use the type(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:)就是获取动态(真实)类型的

  3. 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! 😀
    
  1. 注意的点
    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打印机打印的

3D打印机.jpg

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?()
    
    
    可以调用所有带有@objc的类方法

.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在这级
有啥不对的,欢迎批评指正
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345