认识Swift系列12之对象方法内存

一、这里先看结构体和类的方法区别

先看看一个简单的结构体,可以打开断点调试,发现其调用方法的汇编非常简单 callq 0x100001410

struct Object {
    func eat() { }
}
var o = Object()
o.eat() // 汇编:callq  0x100001410

再看看一个用类来实现相同功能,发现其调用方法的汇编就变的复杂起来 callq *0x70(%rcx),经过进一步跟进断点,发现内部做了非常多的中转操作

class Object {
    func eat() { }
}
var o = Object()
o.eat() // 汇编:callq  *0x70(%rcx)

实际上结构体和类的方法都存储在全局区,和全全局方法没有本质区别,只不过是编译器语法糖特性限制了我们的访问权限而已

可以看出,结构体方法调用非常简单,编译期就决定了调用地址,直接call+地址,而类的方法调用则比价复杂,因为要考虑到继承多态等动态特性,调用地址并不能在编译其确定

因此在设计时,如果某个类只是做一些简单的工作而不考虑复杂的事情时,优先选择结构体

二、对象的内存布局

对于一个对象,内存涉及到三块

  • 1.对象地址内存(可能在堆空间、栈空间、全局区,指向堆空间)
  • 2.对象本身内存(堆空间:分为3块)

    2.1 类信息地址(指向全局区)
    a.类的某些信息
    b.类的对象方法列表(一般按定义顺序存储)
    2.2 引用计数相关
    2.3 属性列表

  • 3.对象信息内存(全局区:分为2块)

    3.1 对象信息
    3.2 对象方法地址列表

如Object类的一个对象

class Object {
    var age = 10
    func eat() {
        print("\(type(of: self))-->\(#function)")
    }
    func run() {
        print("\(type(of: self))-->\(#function)")
    }
    func jump() {
        print("\(type(of: self))-->\(#function)")
    }
 }

此处创建两个对象

var obj1 = Object()
var obj2 = Object()

Swift中,其对象方法实现类似C++虚表方式,其内存布局如下

                              虚表(类似C++)【全局区】
                             ┏━━━━━━━━━━━━━━━━━━━━━━
                             ┃ 类的某些信息
                             ┗━━━━━━━━━━━━━━━━━━━━━━
                             ┃ eat()方法地址
                             ┗━━━━━━━━━━━━━━━━━━━━━━
 对象地址    对象内存【堆空间】   ┃ run()方法地址
 ┏━━━━━┓   ┏━━━━━━━━━━━━━┓   ┗━━━━━━━━━━━━━━━━━━━━━━
 ┃ obj1┃━━>┃ classInfo   ┃━━>┃ jump()方法地址
 ┗━━━━━┛   ┗━━━━━━━━━━━━━┛   ┗━━━━━━━━━━━━━━━━━━━━━━
           ┃ retainCount ┃       ↑
           ┗━━━━━━━━━━━━━┛       ┃
           ┃ var age     ┃       ┃
           ┗━━━━━━━━━━━━━┛       ┃
           ┃ var name    ┃       ┃
           ┗━━━━━━━━━━━━━┛       ┃
                                 ┃
                                 ┃
  对象地址    对象内存【堆空间】     ┃
  ┏━━━━━┓   ┏━━━━━━━━━━━━━┓      ┃
  ┃ obj2┃-->┃ classInfo   ┃━━━━━━┛
  ┗━━━━━┛   ┗━━━━━━━━━━━━━┛
            ┃ retainCount ┃
            ┗━━━━━━━━━━━━━┛
            ┃ var age     ┃
            ┗━━━━━━━━━━━━━┛
            ┃ var name    ┃
            ┗━━━━━━━━━━━━━┛

综上所属

  • 1.所有同类型的对象共享一份虚表(方法列表)
  • 2.对象本身的存储属性和引用计数包含在对象内存中
  • 3.如果子类重写了父类的方法,则子类虚表中存储重写过的方法地址,如果子类没有重写,则子类虚表中会存储父类原本的方法地址

你可能感兴趣的:(认识Swift系列12之对象方法内存)