1.swift强引用
我们编写如下代码
class PWTeacher {
var age:Int = 18
var name:String = "Kody"
}
let t = PWTeacher()
var t1 = t;
var t2 = t
我们经过lldb调试
那么引用计算内都包含什么信息呢,接下来我们用源码探索一下
在HeapObject.cpp文件中的swift_allocObject方法中,我们看下HeapObject()
我们看一下HeapObject的成员变量中
struct HeapObject {
HeapMetadata const *metadata;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
}
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \
InlineRefCounts refCounts
InlineRefCounts----->RefCounts<InlineRefCountBits>-----> InlineRefCountBits---->RefCountBitsT<RefCountIsInline>--->RefCountBitsT---->BitsType bits
BitsType实质是uint64_t所以管理引用计数操作的实质是uint64_t
那我们在创建swiftObject的时候是怎么操作的呢
HeapObject(metadata)--->refCounts(InlineRefCounts::Initialized)
Initialized下面
constexpr RefCounts(Initialized_t)
: refCounts(RefCountBits(0, 1)) {}
我们查看RefCountBitsT的初始化方法
RefCountBitsT(uint32_t strongExtraCount, uint32_t unownedCount)
: bits((BitsType(strongExtraCount) << Offsets::StrongExtraRefCountShift) |
(BitsType(1) << Offsets::PureSwiftDeallocShift) |
(BitsType(unownedCount) << Offsets::UnownedRefCountShift))
{ }
Offsets--->RefCountBitOffsets, RefCountBitOffsets我们可以用下面表示
我们知道t的引用计算应该是3,结合上面的图
33位到62的二进制值为11,十进制位3.
我们在var t1 = t;是swift怎么处理的呢
我们通过汇编调试,看到swift_retain在swift源码中看一下swift_retain具体实现object->refCounts.increment(1);
bool incrementStrongExtraRefCount(uint32_t inc) {
// This deliberately overflows into the UseSlowRC field.
bits += BitsType(inc) << Offsets::StrongExtraRefCountShift;
return (SignedBitsType(bits) >= 0);
}
让我们需要加的1移动到33位,然后加到bits里
2.swift弱引用
class PWTeacher {
var numberOneStudent:PWStudent?
deinit {
print("PWTeacher deinit")
}
}
class PWStudent {
var teacher:PWTeacher?
deinit {
print("PWStudents deinit")
}
}
PWTeacher和PWStudent中相互包含对象,这就会产生引用,我们可以用weak来消除循环引用
func test() {
let teacher = PWTeacher()
weak var teacherWeak = teacher
let student = PWStudent()
student.teacher = teacherWeak
}
test()
weak到底做了什么呢我们汇编调试发现,weak修饰的对象,底层调用的是swift_weakInit,我们去源码看一下,到底做了什么
swift_weakInit------>ref->nativeInit(value)---->object->refCounts.formWeakReference()---->allocateSideTable(true)
我们重点看一下allocateSideTable
HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());
HeapObjectSideTableEntry对象保存实力对象和refCounts
refCounts是SideTableRefCounts类型
SideTableRefCountBits继承自RefCountBitsT,我们在前面分析到RefCountBitsT的成员变量是uint64_t,SideTableRefCountBits的成员变量是uint32_t
在InlineRefCountBits(side)中,会把side的地址指针保存下来
RefCountBitsT(HeapObjectSideTableEntry* side)
: bits((reinterpret_cast<BitsType>(side) >> Offsets::SideTableUnusedLowBits)
| (BitsType(1) << Offsets::UseSlowRCShift)
| (BitsType(1) << Offsets::SideTableMarkShift))
我们知道bits是64位,保存side的规则是 side的地址右移3位,且62,63位保存其他。
var t = Teacher()
weak var tw = t;
print("end")
在print处打断点,看内存布局
(lldb) p t
(RAMLayout.Teacher) $R8 = 0x000000010382e800 (age = 18)
(lldb) x/8gx 0x000000010382e800
0x10382e800: 0x0000000100008158 0xc0000000200e0ad0
0x10382e810: 0x0000000000000012 0x0000000000000000
0x10382e820: 0x00007fff88a9e9f0 0xffffffffffffffff
0x10382e830: 0x0000000800000000 0x0000000000000000
0xc0000000200e0ad0保存side的内存地址,0xc0000000200e0ad0把62和63位设置为0,然后向左平移3位得到0x100705680
(lldb) x/4gx 0x100705680
0x100705680: 0x000000010382e800 0x0000000000000000
0x100705690: 0x0000000a00000003 0x0000000000000002
可知0x000000010382e800为t的地址
weak必须是一个可选类型,才能允许被设置成nil
3.闭包的循环引用
3.1.闭包一般默认捕获外部的变量
func test() {
var age = 10
let clourse = {
age += 1
}
clourse()
print(age)
}
test()
//打印结果是11
3.2.实例变量在闭包中修改
class Teacher {
var age:Int = 18
//反初始化器
deinit {
print("Teacher deinit")
}
}
func test() {
let t = Teacher()
let clourse = {
t.age += 1
}
clourse()
print(t.age)
}
test()
deinit和oc中的dealloc相似,在对象销毁的时候调用。
打印结果
19
Teacher deinit
对于上面的例子我们经过修改
class Teacher {
var age:Int = 18
var finishBlock:(()->())?
deinit {
print("Teacher deinit")
}
}
func test() {
let t = Teacher()
let clourse = {
t.finishBlock = {
t.age = 20
}
}
clourse()
print(t.age)
}
test()
//打印结果
18
从打印结果可以知道deinit没有被调用,产生了循环引用。
t->finishBlock->t
怎么解决循环引用呢
3.3解决循环引用
解决循环引用有两种方式,一种是weak,一种是unowned
weak是弱引用,我们在前面和oc中都有用到,weak修改的变量是可选类型,可以被设置为nil,unowned引用和weak最大的区别是无法被设置成nil。
unowned在运行期间我们都假定有值的,如果经过unowned修饰的实例被置为nil,我们再调用会出现野指针
我们上面的例子修改如下
func test() {
let t = Teacher()
let clourse = {
t.finishBlock = {
[unowned t] in
t.age = 20
}
}
clourse()
print(t.age)
}
我们能看到deinit的打印。
如果我们用weak来改变循环引用
func test() {
let t = Teacher()
let clourse = {
t.finishBlock = {
[weak t] in
t?.age = 20
}
}
clourse()
print(t.age)
}
但是需要注意的是 t?.age = 20。
3.4.捕获列表
定义在参数列表之前,捕获列表被写为用逗号括起来的表达式列表,并用方括号括起来。如果使用捕获列表,则即时省略参数名称,参数类型和返回类型也必须使用in关键字
var age = 0
var height = 0.0
let clourse = {
[age] in
print(age)
print(height)
}
age = 10
height = 1.85
clourse()
打印结果是0和1.85
对于捕获列表中的每个常量,闭包会利用周围范围内具有相同名称的常量和变量,来初始化捕获列表中定义的常量。
4.Swift Runtime探索
4.1纯swift类获取
class PWTeacher {
var name:String = "PW"
func teach() {
print("teach")
}
}
func test() {
var count:UInt32 = 0;
let methods = class_copyMethodList(PWTeacher.self, &count);
for i in 0..<Int(count) {
guard let method = methods?[i] else { return }
let methodString = method_getName(method)
print("方法\(methodString)")
}
var proCount:UInt32 = 0
let propertys = class_copyPropertyList(PWTeacher.self, &proCount);
for j in 0..<Int(proCount) {
guard let property = propertys?[j] else { return }
let propertyName = property_getName(property)
print("成员变量\(String(utf8String: propertyName)!)")
}
}
test()
我们没有打印,我们修改
4.2.@objc修饰
class PWTeacher {
@objc var name:String = "PW"
@objc func teach() {
print("teach")
}
}
我们看打印结果
方法teach
方法name
方法setName:
成员变量name
但是这种,我们类我们无法在oc中访问。
4.2.swift类继承NSObject
class PWTeacher :NSObject{
var name:String = "PW"
func teach() {
print("teach")
}
}
打印结果
方法init
方法.cxx_destruct
我们看 项目-Swift.h文件
@interface PWTeacher : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end
只能看到一个init方法
4.2.swift类继承NSObject并添加@objc关键字
class PWTeacher :NSObject{
@objc var name:String = "PW"
@objc func teach() {
print("teach")
}
}
打印结果
方法teach
方法init
方法name
方法.cxx_destruct
方法setName:
成员变量name
在看项目-Swift.h文件
@interface PWTeacher : NSObject
@property (nonatomic, copy) NSString * _Nonnull name;
- (void)teach;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end
我们可以在oc文件中访问PWTeacher类
4.2.swift类继承NSObject添加dynamic
class PWTeacher :NSObject{
var name:String = "PW"
dynamic func teach() {
print("teach")
}
}
打印结果
方法init
方法.cxx_destruct
即时给方法添加了dynamic,在oc中还是访问不到的该方法的
总结
1.对于纯 Swift 类来说,没有 动态特性。⽅法和属性不加任何修饰符的情况下。这个时候其实已经不具备我们所谓的 Runtime 特性了,这和我们在上⼀节课的⽅法调度(V-Table调度)是不谋⽽合的。 dynamic (动态特性)
2.对于纯 Swift 类,⽅法和属性添加 @objc 标识的情况下,当前我们可以通过 Runtime API 拿到,但 是在我们的 OC 中是没法进⾏调度的。
3.对于继承⾃ NSObject 类来说,如果我们想要动态的获取当前的属性和⽅法,必须在其声明前添加 @objc 关键字,⽅法交换: dynamic的标识。否则也是没有办法通过 Runtime API 获取的。
补充
在swift中默认的基类是SwiftObject在swift源码中可以看到
SWIFT_RUNTIME_EXPORT @interface SwiftObject<NSObject> {
@private
Class isa;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
}
- (BOOL)isEqual:(id)object;
- (NSUInteger)hash;
- (Class)superclass;
- (Class)class;
- (instancetype)self;
- (struct _NSZone *)zone;
...
}
5.反射
反射就是可以动态获取类型、成员信息,在运⾏时可以调⽤⽅法、属性等⾏为的特性。上⾯我们分析过 了,对于⼀个纯 Swift 类来说,并不⽀持我们直接像 OC 那样操作;但是 Swift 标准库依然提供了反射机 制让我们访问成员信息, 反射的⽤法⾮常简单,我们⼀起来熟悉⼀下:
class PWTeacher :NSObject{
var name:String = "PW"
var age = 18
}
func test() {
let mirror = Mirror.init(reflecting: PWTeacher.self);
for pro in mirror.children {
print("\(pro.label ?? "123")--\(pro.value)")
}
print("end")
}
test()
我们只看到打印end,for循环内没有打印,这是因为 Mirror.init内reflecting参数需要传实例
我们修改如下
let mirror = Mirror.init(reflecting: PWTeacher());
//打印结果
name--PW
age--18
end
6.元类型、AnyClass、Self
1.AnyObject:
代表任意类的 instance,类的类型,仅类遵守的协议。
let t = PWTeacher()
let t2:AnyObject = t //类的 instance
let t3:AnyObject = PWTeacher.self //类的类型
protocol PWJsonMap:AnyObject {
}
class PWStudent: PWJsonMap { //仅类遵守的协议,如果是结构体遵守协议就会报错
}
2.Any:
代表任意类型,包括 funcation 类型或者 Optional 类型
var arrary:[AnyObject] = [1,"pw",true]
//编译器会报错,我们修改AnyObject为Any,编译成功
var arrary:[Any] = [1,"pw",true]
3.AnyClass
代表任意实例的类型: AnyObject.Type
4.T.self
如果 T是实例对象,返回的就是它本身; T 是类,那么返回的是 Metadata
5.T.Type
⼀种类型, T.self 是 T.Type 类型
6.type(of:)
⽤来获取⼀个值的动态类型
func test(value:Any) {
let typeValue = type(of: value)
print(typeValue)
}
let age = 10
test(value: age)
//输出是Int
test(value:Any)是静态类型
type(of: value)是动态类型
用继承关系说明这种情况
class PWTeacher :NSObject{
func teach() {
print("PWTeacher teach")
}
}
class PWParTimreTeacher: PWTeacher {
override func teach() {
print("PWParTimreTeacher teach")
}
}
func test(value:PWTeacher) {
value.teach()
print(type(of: value))
}
let teacher = PWParTimreTeacher()
test(value: teacher)
协议下type的情况
protocol PWProtocol {
}
class PWTeacher :PWProtocol{
func teach() {
print("PWTeacher teach")
}
}
func test(value:PWProtocol) {
print(type(of: value))
}
let teacher = PWTeacher()
let t2 :PWProtocol = PWTeacher()
test(value: teacher)
test(value: t2)
打印结果是PWTeacher
我们修改test函数
func test<T>(value:T) {
print(type(of: value))
}
打印结果是
PWTeacher
PWProtocol
这并不是我们想要的,于是我们修改test方法
func test<T>(value:T) {
print(type(of: value as Any))
}
运行后的打印结果是
PWTeacher
PWTeacher