Swift-元类型研究

155 阅读4分钟

swift中围绕元类型实例metadata和元类型的一些特性

AnyObject

  • 代表任意类的实例
  • 代表类的类型
  • 代表类遵守的协议

具体用法

image.png

请看上面代码,LGTeacher是值类型,不是引用类型,所以上面的AnyObject用法都是错误的

正确用法,将LGTeacher改为class就可以

class LGTeacher: MyProtocol {
  var age = 18
}

var t = LGTeacher()

var t1: AnyObject = t.self //任意类的实例
var t2: AnyObject = LGTeacher.self //任意类的类型
var t3: LGTeacher = t2 as LGTeacher

protocol MyProtocol: AnyObject { //类遵循的协议
}

//这样的转换也是允许的
var p:MyProtocol = LGTeacher()
var o:AnyObject = p

AnyObject与具体类型的转换

我们在开发过程中,有时候不知道实例具体的类型,可以用AnyObject代替, 如果知道了类型,可以使用 as as?as!来将AnyObject做类型转换

oc交互的时候的使用

在swift和oc交互的时候,也经常使用AnyObject来做某种类型的instance 例如: var age:AnyObject = t.age as NSNumber

self

用法

  • 当T是任意类的实例,.self返回任意实例本身
  • 当T是类的类型,.self返回任意类的类型,也就是元类型, 就是前面我们提到的metaData

上面的代码,打印t1, t2,以及相关内存格式化: image.png 这就证明了我们上面的说法。

self在方法中的不同表现

class LGTeacher{
   var age = 18

   func test(){
        //当前实例对象
        let t = self
        print(self)
    }

    static func test1(){
        // self 是 LGTeacher 这个类型本身
        let t = self
        print(self)
    }
}

var t = LGTeacher()
t.test()
LGTeacher.test1()

///
(lldb) po t
<LGTeacher: 0x100705bc0>

(lldb) x/8g 0x100705bc0
0x100705bc0: 0x000000010000c268 0x0000000400000003
0x100705bd0: 0x0000000000000012 0x00007ff82ed45a18
0x100705be0: 0x0000000000000000 0x0000000000000000
0x100705bf0: 0x00007ff8524f0002 0x00027ff82ed460c8
LGSwiftTest.LGTeacher
(lldb) po t
LGSwiftTest.LGTeacher

上面打印输出,说明

  • 实例方法中的self。代表的就是当前实例。
  • 类方法中的self。代表的就是当前类的类型本身,即metadata。

我们看看汇编中的信息: image.png image.png 上面汇编代码也能证实,我们两个方法中self的意义。

小写self我们研究过了,那么大写Self呢?

Self

Self不是特定类型,而是让您方便引用当前类型,而无需重复或知道当前类型的名称。

用法

  • 在协议或协议成员声明中,Self是指最终符合协议的类型
  • 方法的返回值
  • 只读下标的返回类型
  • 只读计算属性的类型
  • 可以理解为一个代表当前的Covariant类型

看如下代码:

class LGTeacher {
    static var age = 18
    
    let age1 = age
    var age2 = age
    lazy var age3 = Self.age
    var age4: Int {
        return Self.age
    }
    
    var age5: Int {
        return LGTeacher.age
    }
    
    func test() -> Self {
        //当前实例对象
        return self
    }
}

protocol MyProtocol {
    func get() -> Self
    func set(value: Self)
}

extension LGTeacher: MyProtocol {
    func get() -> Self {
        return self
    }
    
    func set(value: LGTeacher) {
        let t = value.self
        print(value)
        print(t)
    }
}

Any

代表任意类型,包括 function类型或者 Optional类型 与OC中id类型类似 它与AnyObject有本质区别,AnyObject只能代表类类型实例。 例如: image.png

AnyClass

代表任意实例的元类的类型

public typealias AnyClass = AnyObject.Type

X.Type

X.self 也是有自己的类型的,就是X.Type,就是元类型的类型。

注意,这里我们就得出了结论,任意metadata实例,都可以用AnyClass定义, AnyClass = AnyObject.Type = X.Type

打印和汇编分析

image.png image.png 上面的打印结果,以及汇编注解都证实我们的理论。

动态类型(dynamic type)和静态类型(static type)

  • 静态类型:是在编译时就确定的类型
  • 动态类型:是在运行时才能确定的类型
func test(_ value:Any) {
    var ret:Any = type(of: value)
    print(ret)
}

func test1(_ value:AnyObject) {
    var ret:AnyClass = type(of: value)
    print(ret)
}
test(100.0)
test(100)
test1(LGTeacher())
test1(LGPerson())
print("-----end")

///result
Double
Int
LGTeacher
LGPerson

看上面代码: 对于test和test1中的value来说,Any和AnyObject是编译器知道的编译完成的类型,所以叫静态类型,而我们实际运行调用的时候,value的类型是不确定的,这时候通过type(of: )获取的就是他的运行时类型,也就是动态类型。

type(of: )

获取的就是一个值的动态类型。

image.png 看上面代码,可以看到,协议类型是不能用AnyObject来代替的。

image.png 使用Any代替以后,发现答应结果,type(of: )不是TeachProtocol

image.png 使用泛型才实现我们的需求。

所以我个人认为,泛型 比 Any要更广,没研究源码。大家有兴趣研究一下。