preface

Object alloc init has been written for many years, but the internal implementation of alloc only knows that an object is created and a memory space is allocated to store the object. But how does it allocate space, and how much memory does it allocate? How memory allocation relates to classes and so on, so with that in mind, let’s explore the internal implementation of alloc.

preparation

Download objC4 source code from Apple open source website, there are many versions, because the latest version of OBJC4-824 is not available to download now, so I choose objC4 -818.2 version for download. The downloaded code is not directly compiled, you need to configure yourself, the configuration process can see this blog: ios-Underlying Principles 03: OBJC4-781 source compilation & debugging

Begin to explore

1. Alloc and init

    ZFPerson *p = [ZFPerson alloc];
    ZFPerson *p1 = [p init];
    ZFPerson *p2 = [p init];
    
    NSLog(@"p = %@ pAddr = %p",p,&p);
    NSLog(@"p1 = %@ p1Addr = %p",p1,&p1);
    NSLog(@"p2 = %@ p2Addr = %p",p2,&p2);
Copy the code

Printed result

p = <ZFPerson: 0x10123b230> pAddr = 0x7ffeefbff3f8 p1 = <ZFPerson: 0x10123B230 > ·p1Addr = 0x7FfeefBFF3E8 p2 = <ZFPerson: 0x10123B230 > p2Addr = 0x7FfeefBFF3F0Copy the code

From the print we can see that the p1, P2, and p3 Pointers point to the same block of memory, and I drew the following graph just to make it easier to understand

Conclusion: 1. Alloc has the function of allocating memory space. 2

2. Alloc to explore

Create a ZFPerson object, command, click on alloc, follow up inside alloc, notice that inside alloc it calls callAlloc, put a breakpoint on callAlloc and you see that there are a lot of callAlloc calls going on

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

So the question is, do we callAlloc ->callAlloc to execute the alloc->_objc_rootAlloc — >callAlloc? With this in mind, I looked at the stack information

So obviously when you call alloc, instead of calling alloc, you’re calling objc_alloc.(Guess hook processing is done at the bottom level, so we won't explore it for now)And if you continue with the breakpoint, you’ll see that it’s going to go to objc_msgSend(CLS, @selector(alloc)), so it’s going to go to the alloc function

So let me just draw a little bit of a flow diagram here to summarize the alloc process

Now the process goes to the _objc_rootAllocWithZone function

To the _class_createInstanceFromZone function.

3. Core method exploration of alloc process

When you go to the _class_createInstanceFromZone function, you find that it's calculating memory size, allocating space, and connecting the ISA pointer to the class, so this is the core code that implements the alloc inside.Copy the code
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; //2. Create memory 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; } //3. Bind 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); }Copy the code

3.1 Calculating memory size CLS ->instanceSize

Inline size_t instanceSize(size_t extraBytes) const {// Obtain the cache size and align it with 16 bytes if (fastpath(cache.hasFastInstanceSize(extraBytes))) { return cache.fastInstanceSize(extraBytes); ExtraBytes = 0 size_t size = alignedInstanceSize() + extraBytes; // CF requires all objects be at least 16 bytes. if (size < 16) size = 16; return size; }Copy the code

When we enter the instanceSize function, we discover that there are two ways to obtain instanceSize, either from the cache or from the class data

3.1.1 fastInstanceSize

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); }Copy the code

3.1.2 alignedInstanceSize way

// Class' rounded up to a pointer-size boundary. Uint32_t alignedInstanceSize() const {// Unignedinstancesize () return word_align(unalignedInstanceSize()); } // May be unaligned depending on class's ivars. uint32_t unalignedInstanceSize() const { ASSERT(isRealized()); Return data()->ro()->instanceSize; }Copy the code

Calloc (1, size)

3.3 binding isa

The process is to create an ISA pointer associated with the class and finally with obj

objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) { initIsa(cls, true, hasCxxDtor); } inline void objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor) { isa_t newisa(0); // Initialize an ISA pointer newisa.bits = ISA_MAGIC_VALUE; // isa.magic is part of ISA_MAGIC_VALUE // isa.nonpointer is part of ISA_MAGIC_VALUE newisa.has_cxx_dtor = hasCxxDtor; newisa.setClass(cls, this); Extra_rc = 1; // Extra_rc = 1; // This write must be performed in a single store in some cases // (for example when realizing a class because other threads // may simultaneously try to use the class). // fixme use atomics here to guarantee single-store and to // guarantee memory order w.r.t. the class index table // ... but not too atomic because we don't want to hurt instantiation isa = newisa; }Copy the code

4. To summarize

4.1 The process of alloc an object is

Objc_alloc – > callAlloc – > alloc – > _objc_rootAlloc – > callAlloc – > _objc_rootAllocWithZone – > _class_createInstanceFromZone – > (CLS – > InstanceSize (extraBytes))-> calloc(1, size)-> (obj->initInstanceIsa)

4.2 Core steps for alloc an object

  1. Get the memory size of the class
  2. Allocate memory space of size
  3. Associate the allocated space with the ISA of the class