Swift中同样使用自动引用计数(ARC)来管理内存
swift分为strong(强引用类型)、unowned(无主引用类型)、weak(弱引用类型)
强引用(strong)& 无主引用(unowned)
查看swift源码分析强引用与无主引用
内存分布如下
unowned在第1至31位,strong在第33至62位
代码分析
refCount是
0x0000000600000002,引用计数为3,查看sil文件可以查看汇编
在源码中查看
swift_retain
断点调试上面代码
发现在第一个断点处已经有值,说明在新建完实例变量时引用计数就已经为1,再来对照计算器看下
与上面的内存分布图
相呼应
还有一个计算引用计数的函数
用CFGetRetainCount查看引用计数时,引用计数会再+1
弱引用
从上图可以看出经过weak修饰过后的对象是一个可选类型,原来的p不是可选类型,但查看refcount好像看不出引用计数是多少,查看汇编模式
在添加完weak后调用了swift_weakInit,在swift源码搜索查看
0xc0000000200a873e就是HeapObjectSideTableEntry的地址,可以从计算起中查看
先去掉62位和63位,通过上面得知,计算引用计数时使用了向右偏移3位,那计算器向左偏移3位,得到0x1005439F0,再格式化输出
从图中看出输出了p的地址,0x0000000200000002是强引用计数,0x0000000000000002是弱引用计数
闭包的循环引用
定义一个无参数无返回值的闭包,可以发现是与oc中的block是一致的,闭包内部对变量的修改会改变外部原始变量的值在类里定义一个无参无反的闭包,在test方法中实例变量被必报引用着,无法释放,运行后无法打印deinit方法,产生循环引用
如上图可以通过weak和unowned来解决,但是通过unowned修饰的会在整个运行周期都会假定它是有值的,所以会出现野指针的情况,所以要保证生命周期是可控的,
[unowned p] in [weak p] in 这种语法在swift统称为捕获列表,必须定义在参数列表之前,用方括号扩起来,必须带in关键字
捕获列表中的常量会根据上下文来寻找相同的常量名,来初始化捕获列表中的常量,捕获列表中的常量是值拷贝,捕获列表中的常量不可修改
RunTime
在纯的swift类中可以调用oc runtime中的api但这没什么意义,oc也调不到,需要让把swift中的属性和方法都暴漏出去供oc调用需要添加关键字
查看SwiftDemo-Swift.h文件
在文件的最底部
可以看到暴漏出来的属性和方法
再把SwiftDemo-Swift.h头文件引入到oc中,这样就可以调用swift的类属性和方法
- swift是一门静态的语言,对于纯swift类来说没有动态特性,方法和属性不添加任何修饰符时,这个时候不具备runtime特性,只是单纯的v-table调用
- 对于纯swift类,方法和属性添加了@objc能被runtime的api获取到,但没法被oc类调用到
- 对于继承自nsobject的swift来说,如果要让oc类调用到必须在生命前加入@objc关键字,如果想要使用方法交换,必须在方法前添加dynamic关键字,否则暴漏出去后也是不具备动态特性的
查看class_copyMethodList源码
cls是LGTeacher, 意味着cls是swift类,在oc中data是存储着类的信息,进入data
对于swift是有默认的基类的,默认的基类就是SwiftObject,在swift源码中搜索SwiftObject
可以看出SwiftObject继承NSObject协议,并且里面也包含有isa和refcount,之前讲过的TargetAnyClassMetadata继承自TargetHeapMetaData,TargetHeapMetaData只有一个属性kind,TargetAnyClassMetadata有四个属性:isa、superclass、cacheData、data即bits
anyobject
AnyObject
AnyObject代表类的实例对象,类的类型,和仅类所遵守的协议,如果不确定具体是什么类型可以用AnyObject
如果是个结构体遵守JSONMap这个协议,会报错
只有仅类遵守的协议
any
any标示过的数组里面能存入任意类型,比如function,optional,anyobject是any的子集
anyclass
anyclass代表任意实例的类型
T.TYPE T.SELF
当t是实例时t.self存储实例对象,当t是类时,t.self存储这这个类本身
t.type是一种类型,t.self是t.type类型
type(of:)
type(of:)用来获取一个类的动态类型
比如
编译器在编译器任务传入test的是person类型的,实际上是phperson类型的,可以用type(of:)来获取实际类型
声明一个person协议,让phperson遵守person协议,再创建phperson的实例和创建person协议类型的实例,传入test打印出来都是person类型,但是
当test方法改成泛型,value也改成泛型后,打印就变了,
需要把type(of:)里的value强转成any,在有时候有协议和泛型的情况下没办法获取到想要的类型,可以强转成any后再来获取类型