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

2019-07-15  本文已影响0人  Niuszeng

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

先看看一个简单的结构体,可以打开断点调试,发现其调用方法的汇编非常简单 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+地址,而类的方法调用则比价复杂,因为要考虑到继承多态等动态特性,调用地址并不能在编译其确定

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

二、对象的内存布局

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

如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    ┃
            ┗━━━━━━━━━━━━━┛

综上所属

上一篇下一篇

猜你喜欢

热点阅读