iOS进阶之路 (一)OC对象的原理 - alloc

667 阅读3分钟

alloc是iOS中创建对象、开辟内存的方法,本文就学习下alloc在底层做了什么.

1. alloc

代码准备,开始调试。 调试方法:

  • 断点调试
  • 汇编:Debug->Debug Workflow->Always Show Disassembly
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        Person *person = [Person alloc];
    }
    return 0;
}

1.1 _objc_rootAlloc 和 _objc_rootAlloc

+ (id)alloc {
    return _objc_rootAlloc(self);
}
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

1.2 callAlloc

// Call [cls alloc] or [cls allocWithZone:nil], with appropriate 
// shortcutting optimizations.
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        // No alloc/allocWithZone implementation. Go straight to the allocator.
        // fixme store hasCustomAWZ in the non-meta class and 
        // add it to canAllocFasts summary
        if (fastpath(cls->canAllocFast())) {
            // No ctors, raw isa, etc. Go straight to the metal.
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            return obj;
        }
    }    
}
  • if (fastpath(!cls->ISA()->hasCustomAWZ())) hasCustomAWZ意为hasCustomAllocWithZone,判断有没有allocWithZone的实现(只有不是继承NSObjcet/NSProxy的类才为true)
  • if (fastpath(cls->canAllocFast())):内部调用了bits.canAllocFast默认为false。
  • 可以确定此处创建对象会走class_createInstance方法,

1.3 class_createInstance

id 
class_createInstance(Class cls, size_t extraBytes)
{
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}
static __attribute__((always_inline)) 
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
    if (!cls) return nil;

    assert(cls->isRealized());

    // Read class info bits all at once for performance
    bool hasCxxCtor = cls->hasCxxCtor(); // 判断当前class或superclass是否有.cxx_construct 构造方法的实现
    bool hasCxxDtor = cls->hasCxxDtor(); // 判断当前class或superclass是否有.cxx_destruct 析构方法的实现
    bool fast = cls->canAllocNonpointer(); // 判断是否支持优化的isa

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

    id obj;
    if (!zone  &&  fast) {
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor); // 对象size计算
    } 
    else {
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;

        // Use raw pointer isa on the assumption that they might be 
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }

    return obj;
}
  • instanceSize():获取对象的大小
  • calloc():动态开辟内存,接下来的文章会学习
  • initInstanceIsa():初始化isa,接下来的文章会学习

至此,已经完成了初始化isa并开辟内存空间,那我们来看看instanceSize如何获取对象大小的。

1.4 字节对齐

#ifdef __LP64__
#   define WORD_MASK 7UL
#else
#   define WORD_MASK 3UL
#endif

static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

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

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

size_t instanceSize(size_t extraBytes) {
    size_t size = alignedInstanceSize() + extraBytes;
    // CF requires all objects be at least 16 bytes.
    if (size < 16) size = 16;
    return size;
}
  • size_t instanceSize:获取对象的大小
  • lignedInstanceSize():获取类所需要的内存大小
  • unalignedInstanceSize()->data()->ro->instanceSize:获取这个类所有属性内存的大小。这里只有继承NSObject的一个属性isa,所以返回8字节
  • word_align(x + WORD_MASK) & ~WORD_MASK字节对齐算法,64位系统下,对象大小采用8字节对齐。
  • if (size < 16) size = 16:对象大小至少是16字节。

结论:

64位系统下,对象大小采用8字节对齐;
实际申请的内存最低为16字节。

1.5 alloc流程图

  • instanceSize:计算对象大小。
  • calloc:申请开辟空间。
  • initInstanceIsa:指针关联对象。

2. init流程

// Replaced by CF (throws an NSException)
+ (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;
}

init底层没有做什么操作,直接返回了obj。

  • 这样做是一种抽象工厂设计模式,让代码实现更加自由;
  • 我们可以在子类中重写init方法,在重写的init方法中做一些初始化操作。

3. new源码

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

4. 资料

官方源码
Cooci iOS_objc4-756.2 最新源码编译调试