OC 和 Swift 运行时的损耗主要来自于内存管理中对对象的计数,一个对象会不停地进行 retain 和 release, runtime 必须一直 observe 每一个对象的 retain count,当 retain count 达到 0 的时候就释放这个对象.而 C 就没有这个问题,因为 C 没有对象,不需要对对象进行生命周期的内存维护,没有运行时的损耗.
目前主流的内存管理方式分三种:
手动: C 语言的 malloc 库,特点就是无运行时损耗,但不好控制释放内存的时机.关于 C 语言 malloc 和 free 初步了解,移步:C语言 malloc 和 free.
半自动: Objective-C 和 Swift 的 MRC/ARC,有运行时损耗,但基本上可以让程序员不用去考虑内存管理的问题.
自动的: Java/Go 的 GC.基本上同上,但需要在某个时间点去停止所有线程,释放内存的时机不可控.
Objective-C 的 MRC 还需要手动去写 retain/release,在进化到 ARC 之后,除了需要在类成员变量声明的时候,显式地声明是 weak/strong/retain/copy 就可以了, retain/release 的插入交给编译器即可, ARC 其实已经是实际上的自动化内存管理模式了.
而 Swift 在把指针抽象为引用类型,加入 Mutable/Immutable 的概念之后,就只需要偶尔写写 weak 就行了,唯一需要对于内存管理费心的就是 retain cycle 的问题,但也比之前省心很多了.而且随着 Swift 工具链的发展,这些问题都可以在编译期或者 Debug 时就暴露出来.
半自动的内存管理,实际上还有一种,就是 Rust 的 OwnerShip ,这种方式其实是 MRC/ARC 的一种延续,但 MRC/ARC 内存释放的时机还是需要在运行时才能知道,而 Rust 可以在编译期就解析出什么时候可以释放掉内存,从而省略掉 retain/release 的存在,也没必要专门跑一个 runtime 去监测对象的引用计数.从而达到比 ARC 更高的运行效率.仔细思考一下这种自动化的内存管理模式,其实都是在把指针分类,加上 context(上下文),抽象出来,暴露给编译器更多与关于指针的信息,而不是单纯的一个内存地址,从而让编译器可以分析释放对象的时机.
Rust 也不例外,既然要达到比 ARC 更高的运行效率,那就必然要提供给编译器更多的指针信息,以此提供给编译器更多的优化空间,因此在指针声明和传递时都需要显式地声明所有权,代码量也会相应地增多,对于程序员的要求也会更高.
虽然写起来比 ARC 更麻烦一点,但也比 C 那种原始的方式简单很多,提供给了 Rust, Swift 这些”现代编程语言”编写底层程序的可能性.