美文网首页
alloc的探究

alloc的探究

作者: 答案不止一个 | 来源:发表于2020-09-07 17:11 被阅读0次

alloc 分析

LGPerson *p1 = [LGPerson alloc];
id p2 = [p1 init];
id p3 = [p1 init];
        
id p4 = [LGPerson alloc];
id p5 = [LGPerson alloc];
NSLog(@"\n%@\n%@\n%@\n%@\n%@",p1,p2, p3, p4, p5);

<LGPerson: 0x100663f30>
<LGPerson: 0x100663f30>
<LGPerson: 0x100663f30>
<LGPerson: 0x100663f90>
<LGPerson: 0x100664030>

对于alloc 系统会创建一个内存对象,并在栈中创建一个对象指针只想对象的地址空间。
init操作不会创建新的对象空间。

_objc_rootAlloc -> callAlloc -> callAlloc

  1. hasCustomAWZ 代表类有没有实现类方法 + (instancetype)allocWithZone:(struct _NSZone *)zone 如果有这回发送 allocWithZone消息。没有着会去执行_objc_rootAllocWithZone。
  2. slowpath 和fastpath 是编译器优化,大概率执行的优先编译指令
 // [[cls all] init]  new 会调用 objc_alloc_init(Class cls) 方法.再断点调试是可以显示符号
 id objc_alloc_init(Class cls)
{
    return [callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/) init];
}

// Call [cls alloc] or [cls allocWithZone:nil], with appropriate 
// shortcutting optimizations.
static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
    if (slowpath(checkNil && !cls)) return nil;
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        return _objc_rootAllocWithZone(cls, nil);
    }
#endif
    // No shortcuts available.
    if (allocWithZone) {
        return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
    }
    return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}

_objc_rootAllocWithZone -> _class_createInstanceFromZone 其中会具体地初始化一个类

其中有几个重点关注的地方

  1. 计算内存大小 size = cls->instanceSize(extraBytes);
  2. 分配内存空间 obj = (id)calloc(1, size);
  3. 初始化isa obj->initInstanceIsa(cls, hasCxxDtor);
id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
    // allocWithZone under __OBJC2__ ignores the zone parameter
    return _class_createInstanceFromZone(cls, 0, nil,
                                         OBJECT_CONSTRUCT_CALL_BADALLOC);
}
// 
static ALWAYS_INLINE id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{
    ASSERT(cls->isRealized());

    // Read class's info bits all at once for performance
    //hasCxxCtor:类及父类是否有自己的构造函数;
    //hasCxxDtor:类及父类是否有自己的析构函数(这个条件在后面讲对象dealloc的时候也会说到,与对象是否有实例变量有关,这条件会记录在对象的isa内);
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    
    // 使用可以用纯指针表示比如没有实例方法和实例属性的类
    bool fast = cls->canAllocNonpointer();
    size_t size;
    // 获取尺寸, word_align 计算的数据
    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;
    
    // 分配内存 zone 已经废弃
    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        obj = (id)calloc(1, size);
    }
    
    // 
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }
    
    // 是否已经有设置过的标识为或者
    if (!zone && fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (fastpath(!hasCxxCtor)) {
        return obj;
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}
instanceSize

返回的一个对象最少需要的内存大小。分配内存带下的计算:

  1. 内存大小的计算。是根据具体的内核决定,然后是返回 8/16 的整数倍的内存大小。返回的一个对象最少需要的内存大小
  2. 内存大小和类的成员变量有关ivar

调试: x obj 可以打印obj所在内存的内存数据(可能是指针地址,或者数据)。 x/nxg n 表示个数。可以用来显示n个已经组合好的地址数据。

// May be unaligned depending on class's ivars.
    uint32_t unalignedInstanceSize() const {
        ASSERT(isRealized());
        return data()->ro()->instanceSize;
    }

    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() const {
        return word_align(unalignedInstanceSize());
    }

    size_t instanceSize(size_t extraBytes) const {
        if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
            return cache.fastInstanceSize(extraBytes);
        }

        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }

对象的成员数据位置和大小的检测

@interface LGPerson : NSObject
@property (strong, nonatomic) NSString * name;
@property (assign, nonatomic) int age;
@property (assign, nonatomic) int xx;
@property (assign, nonatomic) NSString * flag;
@end

LGPerson *p = [LGPerson alloc] ;
p.name = @"明星";
p.age = 10;
p.flag = @"asdasdasd";
p.xx = 102;

(lldb) x/4xg p
0x10069e4b0: 0x001d8001000022ed 0x000000660000000a
0x10069e4c0: 0x0000000100001010 0x0000000100001030

0x00000066 : 102
0x0000000a : 10
0x0000000100001010: 明星
0x0000000100001030:asdasdasd 
// 说明属性的存储排序不是按照定义的排序,而是进行了优化。
// int四个字节,两个int组合占据8个字节
字节对齐(struct 中的计算)

其实字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:

  1. 数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第⼀个数据成员放在offset为0的地⽅,以后每个数据成员存储的起始位置要从该成员⼤⼩或者成员的⼦成员⼤⼩(只要该成员有⼦成员,⽐如说是数组,结构体等)的整数倍开始(⽐如int为4字节,则要从4的整数倍地址开始存储。 min(当前开始的位置m n)
  2. 结构体作为成员:如果⼀个结构⾥有某些结构体成员,则结构体成员要从其内部最⼤元素⼤⼩的整数倍地址开始存储.(struct a⾥存有struct b,b⾥有char,int ,double等元素,那b应该从8的整数倍开始存储.)
  3. 收尾⼯作:结构体的总⼤⼩,也就是sizeof的结果,.必须是其内部最⼤成员的整数倍.不⾜的要补⻬。
struct P1 {
    BOOL sex; // 2
    NSString * name1; // 8
    int age; // 4
};// 2 + (6 第二个成员8个字节,起始位置从8的整数倍开始开始 ) + 8 + 4 =  20  -> 8(最大成员的)的整数倍 24

struct P2{
    int age; // 4
    NSString * name1; // 8
    BOOL sex; // 2
};// 4 + (4 第二个成员8个字节,起始位置从8的整数倍开始开始 ) + 8 + 2 =  18  -> 8(最大成员的)的整数倍 24
struct P3{
    BOOL sex; // 2
    int age; // 4
    NSString * name1; // 8
};//  2 + (2) + 4 + 8 = 16  -> 16
struct P1 p1 ;
struct P2 p2 ;
struct P3 p3 ;
NSLog(@"%ld %ld  %ld", sizeof(p1), sizeof(p2), sizeof(p3));
// 输出  24  24 16

struct T1{
    int  a; // 8
    char b;
    struct P1 p;// 24 最大成员8
};// 4 + 2 + (2) + 24 = 32 -> 32

struct T2{
    char b;
    struct P1 p;// 24 最大成员8
};// 2 + (6 P1 的最大成员是8 起始从8的倍数开始) + 24 = 32 -> 32

struct T3{
    int  a; // 8
    char b;
    struct P4 p4; // 8 最大成员是4
};// 4 + 2 + (2) + 8 = 16 -> 16  最大成员是4 4的倍数

struct T4{
    char b;
    struct P4 p4; // 8 最大成员是4
};//  2 + (2) + 8 = 12 ->  最大成员是4,所以是4的倍数。12
calloc 内存分配

在libmalloc中。分配内存按照16字节进行分配。根据前面计算的大小 size。

(size + 15) >> 4 // 计算大于size的16的最小整数倍
initInstanceIsa

主要负责初始化 对象的isa数据。
alloc过程中对象isa的初始化

init

对于init函数,

总结

malloc的执行流程


image
  1. 对象大小的分配以及 字节对齐
  2. 分配空间和16字节对齐
  3. 初始化isa

参考:

  1. Runtime初始化alloc
  2. OC源码编译
  3. IOS的isa指针优,主要介绍isa_t和其中的标志位
  4. 深入理解内存分配
  5. 内存对其
  6. (转) 内存对齐
__builtin_expect

cpu 的去指令和执行指令是并行的。如果执行指令后发现要跳转。可能要重新取指令降低效率。所以__builtin_expect可以设置一个概率比较大的跳转的位置,提高取指令执行的效率。

// 比如。bool运算下可能下面的第二条指令执行的概率高。设置1。直接选取第二条执行。减少再次取指令的几率。
#define fastpath(x) (__builtin_expect(bool(x), 1))
malloc分析

相关文章

  • alloc探究

    关于alloc后指针内存 首先,我们先创建3个不同指针,并且打印他们的内存地址 打印结果如下: 我们发现,三个指针...

  • alloc的探究

    alloc 分析 对于alloc 系统会创建一个内存对象,并在栈中创建一个对象指针只想对象的地址空间。init操作...

  • 01-OC对象的本质

    探究:1.objc_alloc流程2.init操作3.开辟内存 alloc开辟内存. objc_alloc流程1....

  • iOS底层探究-04:NSObject的alloc源码分析

    在上篇文章我们探究了自定义类的alloc源码,接下来我们探究下NSObject的alloc源码,我们会发现她并不会...

  • OC底层-ISA的前生今世

    在前面的OC底层-对象的alloc流程探究文章中,alloc的流程中,我们知道了OC底层是通过initInstan...

  • iOS 底层探究之 alloc

    我们通过几个问题来探究下一个iOS如何获取到一个对象: alloc和init的区别? alloc方法做了哪些事情?...

  • 002-OC对象原理探究 - 结构体内存对齐

    引言 上一篇文章,探究 alloc的过程中,提到内存对齐。下面我们通过对OC对象QLPerson探究来展开内存对齐...

  • 003-OC对象原理探究 - isa 和 nonpointer

    引言 在001-OC对象原理探究 - alloc[https://www.jianshu.com/p/c09751...

  • iOS底层原理探究03-calloc探究

    通过对《iOS底层原理探究01-alloc底层原理》[https://www.jianshu.com/p/aafb...

  • 类-初探

    alloc干了什么 在探究这个问题前,需要现有一份可编译的源码[https://opensource.apple....

网友评论

      本文标题:alloc的探究

      本文链接:https://www.haomeiwen.com/subject/hlxlektx.html