关于Objective-C对象初始化的一些探索

言归正传,以下代码,系统到底帮我们做了些什么?

NSObject *object = [[NSObject alloc] init];

通过苹果官方的开源代码,我们可以看得到内部的实现机制;
官方地址:opensource.apple.com
配置调试:https://blog.csdn.net/myiphon/article/details/105014942
可编译的779版本:https://github.com/DuWen/Objc4

Alloc

  • _objc_rootAlloc
  • callAlloc
  • allocWithZone
  • _objc_rootAllocWithZone
  • _class_createInstanceFromZone
/***********************************************************************
* class_createInstance
* fixme
* Locking: none
*
* Note: this function has been carefully written so that the fastpath
* takes no branch.
**********************************************************************/
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;

    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    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来分配内存大小

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;
    }

可以看出不足16字节的默认设置了16字节的大小,也就是说一个对象的创建至少占16个字节,而且size做了字节对齐操作

uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }
static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}
#   define WORD_MASK 7UL

为了提高CPU的访问效率,这里保证了Size是8的倍数。
x + WORD_MASK 保证了不足8位需要进位
& ~WORD_MASK 保证后三位是0 也就是8的倍数

通过calloc分配所需的内存空间,并返回一个指向它的指针,查看源码的时候看到了calloc、malloc、realloc,那他们有什么区别呢?以下是定义的函数原型

void	*malloc(size_t __size) __result_use_check __alloc_size(1);
void	*calloc(size_t __count, size_t __size) __result_use_check __alloc_size(1,2);
void	*realloc(void *__ptr, size_t __size) __result_use_check __alloc_size(2);

calloc & malloc : 分配所需的内存空间,并返回一个指向它的指针
realloc:尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小
区别:
1、malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。
2、malloc与calloc用来动态分配内存空间,而realloc则是对给定的指针所指向的内存空间进行扩大或者缩小。

init

  • _objc_rootInit
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;
}

可以看出init并未做任何操作。。。

验证

新建个Person类,里面有age、name属性

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

打印下实例对象的成员变量多占大小以及指针指向的内存大小

Person *person = [[Person alloc] init];
NSLog(@"%zd", class_getInstanceSize([person class]));
NSLog(@"%zd", malloc_size((__bridge const void *)person));
// 打印信息
2020-04-07 17:38:19.974677+0800 objc-debug[23494:214167] 24
2020-04-07 17:38:19.975183+0800 objc-debug[23494:214167] 32

实例对象的isa指针 占8个字节
age是Int类型 占4个字节
name是String类型 占 8个字节
总共20个字节,那怎么会输出24呢?
上面分析源码时,发现有字节对齐的操作,那我们来试试字节对齐后的值是多少
20转成二进制是:00010100 代入公式计算
(0001 0100 + 0000 0111) & 1111 1000 = 0001 1000 也就是24

那指针指向的内存大小又为啥是32呢?
在上面的源码中,我们发现这么一段代码

obj = (id)calloc(1, size);

calloc到底做了些什么骚操作,改变了内存大小,通过符号断点可以跟踪到malloc源码,这也是苹果开源的代码 libmalloc-283.40.1

calloc

  • calloc
  • malloc_zone_calloc
  • zone->calloc(zone, num_items, size)
  • default_zone_calloc (malloc.c)
  • zone->calloc
  • nano_calloc (nano_malloc.c)
  • _nano_malloc_check_clear
  • segregated_size_to_fit
    最后定位到segregated_size_to_fit中,发现输出的内存大小变化了
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
	size_t k, slot_bytes;

	if (0 == size) {
		size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
	}
	k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
	slot_bytes = k << SHIFT_NANO_QUANTUM;							// multiply by power of two quanta size
	*pKey = k - 1;													// Zero-based!

	return slot_bytes;
}
#define SHIFT_NANO_QUANTUM		4
#define NANO_REGIME_QUANTA_SIZE	(1 << SHIFT_NANO_QUANTUM)	// 16

根据上面的公式计算
(24 + 16 - 1)>>4<<4 也就是32!!!
也就是说对原始内存大小做了16字节对齐,确保了是16的倍数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值