iOS 底层探究一:alloc

这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战

在咱们平常开发过程当中,对象初始化应该是咱们每一个人常常遇到的,可是对象初始化过程当中具体作了哪些事情,也许不少人不明因此,因此咱们来探究一下对象初始化的过程。c++

首先,咱们经过一个案例看一下缓存

2936157-11cca52c33527695.png

咱们建立了一个 LGPerson对象,对 p1alloc 操做,对 p2p3分别作 init 操做,而后分别打印 p1p2p3及它们的地址与指针的地址,而后咱们能够看到p1p2p3的地址都同样,可是他们的指针地址却不同。能够得出结论:markdown

1.LGPerson 类在执行完 alloc方法以后就开辟了内存空间,有了指针的指向。 2.p2p3p1指向的内存相同,说明 init 方法没有对指针作操做。 3.在执行完init方法后咱们经过对p1p2p3指针取地址打印,能够看到指针地址并不相同,且是以8个字节的间隔连续存储在栈空间。函数

如今咱们想点进去看看 alloc 跟 init 方法的具体实现,咱们能够看到都是只有声明没有实现。这里咱们对  alloc 方法打断点,经过 Debug Workflow 生成汇编代码能够看到,这里调用的是 objc_alloc 方法。post

allco 方法源码调用流程

咱们知道底层调用的是objc_alloc方法,咱们下载objc的开源库来看下具体的实现。ui

  1. [LGPerson alloc]

首先第一步在起始位置打上断点。spa

2936157-62a35cb9859dc176.png

  1. [NSObject alloc] 第二步咱们看到来到了NSObject类的alloc方法,因为全部类都是继承于NSObject类。

2936157-7bd57b5d8cc8eafe.png

  1. objc_alloc

2936157-91e5ea73fbca5a44.png

这一步咱们会遇到一些问题,由于第二步命名调用的是 _objc_rootAlloc 放到,可是这里却来到了 objc_alloc 方法里面。那么为何会走到这里了呢?咱们在以下代码中能够找到答案。3d

2936157-29b48a20745c9ca1.png

  1. callAlloc

2936157-616efa267dbd8eb9.png

这里能够看到在 1931 行有个判断,第一次调用的时候并无走到 1932 行,而是在 1940 行对 cls 也就是 LGPerson 类发送了 alloc 方法。指针

  1. _objc_rootAlloc

2936157-079849c29909015e.png

2936157-11f2fbf31ed1610d.png

这里就会先进入 alloc 方法,而后调用 _objc_rootAlloc 方法。code

  1. _objc_rootAllocWithZone

2936157-d926b91e91dfa45b.png

如今就再次进入了 callAlloc 方法,这也是 callAlloc 方法调用两次的缘由。此次会走到 1932 行,调用 _objc_rootAllocWithZone 方法。

  1. _class_createInstanceFromZone

2936157-96719736e889edbf.png

2936157-96719736e889edbf.png 在 _objc_rootAllocWithZone 方法以后会进入到  _class_createInstanceFromZone 方法,在这个方法里面咱们着重关注几个地方,在 7977 行这个会计算出 LGPerson 类相应的内存大小;7984 行会在堆区开辟出 size 大小的内存空间;7994 会绑定 isa 与 LGPerson 类相关联, 并赋值了 hasCxxDtor c++ 相关的方法与函数;8002 行返回 obj 对象。

那么问题来了,是如何知道该开辟多大的内存空间呢?具体咱们来看一下 instanceSize 方法。

**

inline size_t instanceSize(size_t extraBytes) const {
        // 若是有缓存会返回缓存的 size 大小
        if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
            return cache.fastInstanceSize(extraBytes);
        }
        
        // 若是没有缓存,会经过 alignedInstanceSize() + extraBytes 这一步来计算 size 大小
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }
复制代码

经过 instanceSize 方法咱们能够看到,若是有缓存会返回缓存的 size 大小, 若是没有就会经过 alignedInstanceSize() + extraBytes 这一步来计算 size 大小。开辟空间的大小与类的成员变量有关。继承于 NSObject 的类都会有一个 isa 成员变量。若是当前类没有其余成员变量,返回的 size 大小就是 isa 的大小8 字节。可是由于 8 本身小于 16 字节,根据字节对齐原则,最后返回的是 16 字节。

**

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
复制代码

经过代码也能够看到,isa 是 Class 类型,objc_class 是一个结构体类型。isa 是结构体指针类型,因此是 8 个字节。objc_class 继承与最原始的类型 objc_object

2936157-d75b1e91e8b8cd6e.png

最后咱们经过打印能够看到对象内存的首字节就 isa 及当前对象的内存状况。

allco 方法调用流程图

能够看到依次走入顺序

objc_alloc → callAlloc → objc_msgSend → alloc → _objc_rootAlloc → callAlloc→ _objc_rootAllocWithZone

2936157-09cd490fa108a16f.png

相关文章
相关标签/搜索