iOS 进阶之底层原理一OC对象原理alloc做了什么

人狠话不多,直接上干货。这是第一篇,之后还会持续更新,当作自己学习的笔记,也同时分享给大家,希望帮助更多人。

首先,我们来思考,下面这段代码的输出是否相同。答案很明显,p1、p2、p3是指向相同的对象,但是指针地址是不同的。那么问题来了,为啥呢。保留这个疑问,我们来探索alloc和init到底在底层到底做了什么。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    FFPerson *p1 = [FFPerson alloc];
    FFPerson *p2 = [p1 init];
    FFPerson *p3 = [p1 init];

    FFLog(@"p1 --- %@ - %p",p1,&p1);
    FFLog(@"p2 --- %@ - %p",p2,&p2);
    FFLog(@"p3 --- %@ - %p",p3,&p3);
}

p1 --- <FFPerson: 0x2818cb7e0> - 0x16f501c68
p2 --- <FFPerson: 0x2818cb7e0> - 0x16f501c60
p3 --- <FFPerson: 0x2818cb7e0> - 0x16f501c58

我们在alloc这里打断点,这里要用真机测试,为了arm64架构。断点到这后,按住control这个调试按钮会变,点进去,好像要点两下,就会进入一段汇编,圈中的就是底层的实现入口了。

接下来我们就进入源码解析了,苹果这个源码是开源的,苹果开源库,从这里下载objc4的最新源码。直接全局搜索objc_alloc,我们发现会有很多,下面那张图展示为实现部分。

一步步找下去,_objc_rootAllocWithZone --> _class_createInstanceFromZone。这里大致内容就是创建对象,并且分配了大小,初始化isa。这里有个算法,实例化的对象的大小为8的倍数。

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;
    }
// 初始化isa
    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);
}

size_t instanceSize(size_t extraBytes) {
// size = 8 + 0
        size_t size = alignedInstanceSize() + extraBytes;
        if (size < 16) size = 16;
        return size;
    }
uint32_t alignedInstanceSize() const {
// 内存对齐,为了读取方便
        return word_align(unalignedInstanceSize());
    }
uint32_t unalignedInstanceSize() const {
        ASSERT(isRealized());
// 内部含有一个isa 
        return data()->ro()->instanceSize;
    }
// 进行内存对齐,最后为8的倍数
static inline uint32_t word_align(uint32_t x) {
    // 7 + 8 = 15
    // 0000 1111

    // 1111 1000
    //&0000 1000
    
    // 0000 0111
    
    return (x + WORD_MASK) & ~WORD_MASK;
}

同理,我们在init那边打断点,发现进入的是objc_retain,这里我们发现,非taggedPointer的,直接引用计数加一,但是对象还是原来的对象,所以为什么p1、p2、p3指向的对象相同。

id 
objc_retain(id obj)
{
    if (obj->isTaggedPointerOrNil()) return obj;
    return obj->retain();
}

根据源码分析,实例对象的大小会不等于实际分配的大小,就是因为内存对齐,详情可以看我源码的注释。接着我们来探索calloc底层到底做了什么。这里将移步libmalloc源码库了。

void *
calloc(size_t num_items, size_t size)
{
	return _malloc_zone_calloc(default_zone, num_items, size, MZ_POSIX);
}
static void *
_malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size,
		malloc_zone_options_t mzo)
{
    // 删除部分不需要的判断代码
    // 这里会跳转default_zone_calloc
	ptr = zone->calloc(zone, num_items, size);
    // 删除部分不需要的判断代码
	return ptr;
}

static void *
default_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
	zone = runtime_default_zone();
	// nano_calloc
	return zone->calloc(zone, num_items, size);
}
static void *
nano_calloc(nanozone_t *nanozone, size_t num_items, size_t size)
{
	size_t total_bytes;

    // 删除部分不需要的判断代码
	if (total_bytes <= NANO_MAX_SIZE) {
        // 这里是核心
		void *p = _nano_malloc_check_clear(nanozone, total_bytes, 1);
		if (p) {
			return p;
		} else {
			/* FALLTHROUGH to helper zone */
		}
	}
	malloc_zone_t *zone = (malloc_zone_t *)(nanozone->helper_zone);
	return zone->calloc(zone, 1, total_bytes);
}
_nano_malloc_check_clear(nanozone_t *nanozone, size_t size, boolean_t cleared_requested)
{
	MALLOC_TRACE(TRACE_nano_malloc, (uintptr_t)nanozone, size, cleared_requested, 0);

	void *ptr;
	size_t slot_key;
	size_t slot_bytes = segregated_size_to_fit(nanozone, size, &slot_key); // Note slot_key is set here
    // 删除部分不需要的判断代码
	return ptr;
}

static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
	size_t k, slot_bytes;
    
// NANO_REGIME_QUANTA_SIZE 为16
// SHIFT_NANO_QUANTUM 4
// 这里就是左移右移算法,最终为16的倍数
	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;
}

对象创建原理已经知道了,接下来就是对象的结构了。下一篇文章将会分析对象的本质,isa的结构,已经对象与isa的关系。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值