Recently the company is not so busy, idle nothing to read the nuggets article, look tired, oneself also want to start to write what, write the bottom thing is very boring, want to write well, write easy to understand is not easy, but people have to try.

1. Alloc assembly check

1.1. Alloc view directly

So let’s just click on alloc and look at the source code.

+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
Copy the code

All we know from here is that NSObject has this method alloc in it. Then you can’t see anything. Equivalent to.h files, but not.m implementations.

Then we’ll have to do it another way.

1.2.alloc assembly view

Go to Debug -> Debug Workflow -> Always Show Disassembly. The breakpoint opens and goes into assembly mode.

Alloc an object.

Person *person = [Person alloc];
Copy the code

Break the point at alloc, run the code

->  0x107bd5e83 <+51>:  movq   0x762e(%rip), %rax        ; (void *)0x0000000107bdd508: Person
    0x107bd5e8a <+58>:  movq   %rax, %rdi
    0x107bd5e8d <+61>:  callq  0x107bd63f6               ; symbol stub for: objc_alloc
Copy the code

So let’s focus, so you can see objc_alloc, ok, so let’s add that to the breakpoint as well.

Run it again, and we can see the code

libobjc.A.dylib`objc_alloc:
->  0x7fff20190b4d <+0>:  testq  %rdi, %rdi
    0x7fff20190b50 <+3>:  je     0x7fff20190b6c            ; <+31>
    0x7fff20190b52 <+5>:  movq   (%rdi), %rax
    0x7fff20190b55 <+8>:  testb  $0x40, 0x1d(%rax)
    0x7fff20190b59 <+12>: jne    0x7fff20188a75            ; _objc_rootAllocWithZone
    0x7fff20190b5f <+18>: movq   0x66bbf952(%rip), %rsi    ; "alloc"
    0x7fff20190b66 <+25>: jmpq   *0x5fe8d124(%rip)         ; (void *)0x00007fff20173780: objc_msgSend
    0x7fff20190b6c <+31>: xorl   %eax, %eax
    0x7fff20190b6e <+33>: retq  
Copy the code

The _objc_rootAllocWithZone that we can see. Go into this method and check it out.

libobjc.A.dylib`_objc_rootAllocWithZone:
->  0x7fff20188a75 <+0>:   pushq  %rbp
    0x7fff20188a8b <+22>:  testw  %si, %si
    0x7fff20188a8e <+25>:  je     0x7fff20188aac            ; <+55>
    0x7fff20188a90 <+27>:  movl   $0x1, %edi
    0x7fff20188a95 <+32>:  callq  0x7fff2019323a            ; symbol stub for: calloc
Copy the code

We can see that calloc is called, and we know that we’re opening up memory, allocating memory. But we can’t see anything more specific through assembly.

So what do we do? Let’s see.

2. View the source code

The source code is available through Apple’s openSource.

2.1. Alloc

In the nsobject. mm file, we can finally see the implementation of alloc.

+ (id)alloc {
    return _objc_rootAlloc(self);
}
Copy the code

2.2. _objec_rootAlloc

Click on the _objec_rootAlloc method to check it out.

2.3. CallAlloc

This is where we go to callocAlloc.

static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, bool allocWithZone=false) { if (slowpath(checkNil && ! cls)) return nil; #if __OBJC2__ if (fastpath(! cls->ISA()->hasCustomAWZ())) { 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; } } #endif if (allocWithZone) return [cls allocWithZone:nil]; return [cls alloc]; } obj = class_createInstance(CLS, 0);Copy the code

2.3. Class_createInstance

So if we look at the code and we get to the class_createInstance method, keep going

id 
class_createInstance(Class cls, size_t extraBytes)
{
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}
Copy the code

2.4. _class_createInstanceFromZone

So let’s go to the _class_createInstanceFromZone method.

static __attribute__((always_inline)) id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct = true, size_t *outAllocatedSize = nil) { 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); } else { if (zone) { obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size); } else { obj = (id)calloc(1, size); } if (! obj) return nil; obj->initIsa(cls); } if (cxxConstruct && hasCxxCtor) { obj = _objc_constructOrFree(obj, cls); } return obj; }Copy the code

Return obj object.

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

Calloc memory allocation is obviously the same as assembly analysis. How much memory is allocated?

Size_t size = CLS ->instanceSize(extraBytes);

2.5. InstanceSize

Let’s go in and look at the instanceSize method.

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

The preliminary conclusion is that if the size is less than 16, return 16.

Let’s look at the alignedInstanceSize method again.

    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }
Copy the code

You can see that there are memory alignment methods.

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


#   define WORD_MASK 7UL
Copy the code

WORD_MASK here is equal to 7, the x we passed in is 8, why is 8, we’ll talk about later.

So what do we do here? What do we do here

7 is 0000 01111, if ~ is inverse, it is 1111 1000. 15 is 0000 1111.

1111 1000&0000 1111 is 0000, 1000, 0000, 1000 is 8.

You can conclude that memory alignment is 8 bytes.

If (size < 16) size = 16; So the size is 16 bytes.

So why do we do this? We trade space for time and increase efficiency.

2.6. InitInstanceIsa

Let’s keep going

obj->initInstanceIsa(cls, hasCxxDtor);
Copy the code

After obJ allocates memory, it does not know which class it corresponds to, so it calls initInstanceIsa.

objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) { assert(! cls->instancesRequireRawIsa()); assert(hasCxxDtor == cls->hasCxxDtor()); initIsa(cls, true, hasCxxDtor); }Copy the code

Here we look at the initIsa method.

inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) { assert(! isTaggedPointer()); if (! nonpointer) { isa.cls = cls; } else { assert(! DisableNonpointerIsa); assert(! cls->instancesRequireRawIsa()); isa_t newisa(0); #if SUPPORT_INDEXED_ISA assert(cls->classArrayIndex() > 0); newisa.bits = ISA_INDEX_MAGIC_VALUE; newisa.has_cxx_dtor = hasCxxDtor; newisa.indexcls = (uintptr_t)cls->classArrayIndex(); #else newisa.bits = ISA_MAGIC_VALUE; newisa.has_cxx_dtor = hasCxxDtor; newisa.shiftcls = (uintptr_t)cls >> 3; #endif isa = newisa; }}Copy the code

The important thing here is that isa.cls = CLS;

That means associating obj with our Person class, which I’ll write a whole article about later.

And as we all know, our NSObject class is just holding this ISA pointer.

@interface NSObject <NSObject> { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wobjc-interface-ivars"  Class isa OBJC_ISA_AVAILABILITY; #pragma clang diagnostic pop }Copy the code

The ISA pointer takes up 8 bytes, which is why the x passed above by memory alignment is 8, because we have no other attributes for Person but isa.

So at this point, we have a pretty good idea of how this works.

3. The init and new

3.1. The init

Normally we just alloc init, so we alloc can return an object, so what does init do

+ (id)init {
    return (id)self;
}

- (id)init {
    return _objc_rootInit(self);
}


id
_objc_rootInit(id obj)
{
    return obj;
}
Copy the code

Think of it, or back to yourself, that would be doing it to kill a dead horse, you think

That’s definitely not true. It’s apple’s way of making extensions for us developers

- (instancetype)init { self = [super init]; If (self) {return self; }Copy the code

Now we get the point.

3.2. The new

So normally we don’t alloc init, we just replace it with new, so what does new do

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

Boy, just call callAlloc and init, isn’t that alloc init?

4. To summarize

I’m getting tired of looking at all the text and code above, so let’s summarize the alloc flow with a diagram.

So here are a few questions.

  1. So what’s in the ISA here?
  2. What does calloc do?
  3. How does memory alignment work? How does memory alignment work?

These questions will be addressed in the next article, so stay tuned.