美文网首页
iOS-底层原理-01 alloc init new作用和源码分

iOS-底层原理-01 alloc init new作用和源码分

作者: 如意神王 | 来源:发表于2021-09-20 19:41 被阅读0次

1.通过简单的小例子看看 alloc 和 init的作用

1.代码输出

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    Person * p1 = [Person alloc];
    Person * p2 = [p1 init];
    Person * p3 = [p1 init];
    
    NSLog(@"p1 == %@ p1 == %p &p1 == %p", p1, p1, &p1);
    NSLog(@"p2 == %@ p2 == %p &p1 == %p", p2, p2, &p2);
    NSLog(@"p3 == %@ p3 == %p &p3 == %p", p3, p3, &p3);
}

2.输出结果

p1 == <Person: 0x282448110> p1 == 0x282448110 &p1 == 0x16cf81bf8
p2 == <Person: 0x282448110> p2 == 0x282448110 &p1 == 0x16cf81bf0
p3 == <Person: 0x282448110> p3 == 0x282448110 &p3 == 0x16cf81be8

3.结论
p1、p2、p3三个指针指向相同的内存地址 0x2812fbe80 说明如下
1.init对开辟的内存空间没有做任何处理
2.开辟的内存地址来自于alloc方法
3.&p3 &p2 &p1依次累加,说明栈上的空间连读且指针为8字节

2. alloc源码调用流程

1.通过调试编译好的源码,alloc调用流程如下
alloc --> _objc_rootAlloc --> callAlloc --> _objc_rootAllocWithZone --> _class_createInstanceFromZone --> instanceSize、calloc、initIsa

2.alloc流程源码

  1. alloc源码
+ (id)alloc {
    return _objc_rootAlloc(self);
}
  1. _objc_rootAlloc源码
id  _objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
  1. callAlloc源码
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));
}

4._objc_rootAllocWithZone 源码

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);
}
  1. _class_createInstanceFromZone源码
    整个流程调用下来最重要的三个方法如下,恰好诠释了alloc到底做了哪些事情
    _class_createInstanceFromZone --> instanceSize、calloc、initIsa
    1.instanceSize 先计算出需要的内存空间大小
    2. calloc 向系统申请开辟内存,返回地址指针,并没有指向对应的类,仅仅开辟了内存空间
    3. initIsa 关联到相应的类,和对应的class关联起来
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
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
    size_t size;

    // 1.计算出需要开辟多少字节的内存地址
    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        // 2.开辟内存空间,返回地址指针,此时并没有关联到对应的类只是一个地址
        // 2.这里自动开辟的内存就是16字节对齐的,malloc源码里面有 先右移4位小于16抹零,再左移4位
        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.
        // 3.通过isa指针把上面开辟的内存地址关联到对应的类
        obj->initIsa(cls);
    }

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

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

6.开辟的内存空间大小(字节对齐,8字节对齐,最少16个字节)
1.没有字节对齐的空间传递到函数里,返回有字节对齐的空间大小,且最少16个字节
2.alignedInstanceSize() ---> word_align(unalignedInstanceSize())
3.unalignedInstanceSize()--->类的数据段data()->ro()->instanceSize
4.字节对齐的算法(x + WORD_MASK) & ~WORD_MASK
8的倍数,WORD_MASK = 7 (x + WORD_MASK)任何一个数加上7,都给个机会~WORD_MASK = ~7 = 1000,即使8以下抹零 速度很快
5.(x + 7) >> 3 << 3
6. if (size < 16) size = 16; 以8字节对齐为基准,最少16个字节
7.对象需要的内存空间大小 8的倍数 8字节对齐
8.最少16个字节,为了安全预留8字节的空间,尽管不需要也最少16个字节
9.为什么8字节对齐,以空间换取时间,可长可短的话读起来效率非常低,8字节指针非常多,都是8字节效率很高
10.苹果早期版本是8字节对齐,新版是16字节对齐fastInstanceSize ---> align16
11.为什么16字节,方便速度便捷,16字节更加安全

    inline 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;
    }
    size_t fastInstanceSize(size_t extra) const
    {
        ASSERT(hasFastInstanceSize(extra));

        if (__builtin_constant_p(extra) && extra == 0) {
            return _flags & FAST_CACHE_ALLOC_MASK16;
        } else {
            size_t size = _flags & FAST_CACHE_ALLOC_MASK;
            // remove the FAST_CACHE_ALLOC_DELTA16 that was added
            // by setFastInstanceSize
            return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
        }
    }

static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}
    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() const {

        return word_align(unalignedInstanceSize());
    }
    
    uint32_t unalignedInstanceSize() const {
        ASSERT(isRealized());
        return data()->ro()->instanceSize;
    }

字节对齐的算法

#ifdef __LP64__
#   define WORD_SHIFT 3UL
#   define WORD_MASK 7UL
#   define WORD_BITS 64
#else
#   define WORD_SHIFT 2UL
#   define WORD_MASK 3UL
#   define WORD_BITS 32
#endif
static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

7.对象属性在内存中的布局(读取对象属性的值)
1.对应类的属性

@interface Person : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@property (nonatomic, assign) NSString * hobby;
@end

2.创建类对象赋值

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        
        Person * person = [Person alloc];
        person.name = @"daShen";
        person.age = 18;
        person.height = 185;
        person.hobby = @"女";
        NSLog(@"person == %@", person);

    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

3.读取情况
1.x person 16进制读取对象的内存空间
2.x/4gx person 读取4段内存地址,而且是排列好的
3.栈顶是isa指针
4.(lldb) x/4gx person
0x60000290c7e0: 0x0100000104759571 0x0000000000000012
0x60000290c7f0: 0x0000000104754028 0x00000000000000b9
(lldb) po 0x60000290c7e0
<Person: 0x60000290c7e0>

底层大师班内存读取情况.jpeg

3.init和new到底做了什么

1.init 什么都没有做,直接返回了obj对象,只是方便重写,工厂设计模式,提供接口层,更符合真实需求去设计
1.init 源码

+ (id)init {
    return (id)self;
}
- (id)init {
    return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
    // In practice, it will be hard to rely on this function.
    // Many classes do not properly chain -init calls.
    return obj;
}

2.new源码
new = [alloc init] 完全一致 new = [callAlloc() init]

+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}

4.总结

1.alloc 的作用
1.1先计算出需要的内存空间大小
1.2向系统申请开辟内存,返回地址指针
1.3关联到相应的类

2.init的作用
2.1直接返回内存地址,构造方法,工厂设计模式,提供接口层

3.new的作用
3.1 new 完全等价于 [alloc init]

相关文章

网友评论

      本文标题:iOS-底层原理-01 alloc init new作用和源码分

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