preface

You use objects every day, you create objects. But we don’t know how apple’s bottom line is to create objects, how the alloc method is implemented. I decided to take a break and explore apple’s underlying code

The target

  1. What’s the order in which we’re going to find the source code for alloc?

The first step — to learn to debug methods, find the entrance, step by step to explore.

Preparation: We create a new ios project and write the following code in viewController. m’s viewDidLoad method (the code in this article is based on arm64 real machine debugging)

    LGPerson *p1 = [LGPerson alloc];
    LGPerson *p2 = [p1 init];
    LGPerson *p3 = [p1 init];

    NSLog(@"%@-%p-%p",p1,p1,&p1);
    NSLog(@"%@-%p-%p",p2,p2,&p2);
    NSLog(@"%@-%p-%p",p3,p3,&p3);
Copy the code

Method -1. We first through the breakpoint mode, control+stepInfo into the method

Dyld_stub_binder = ld_STUB_binder = dyLD_stub_binder = dyLD_stub_binder = dyLD_stub_binder = dyLD_stub_binder

Further down, we find objc_msgSend

Sorry, I broke it on the init line, stepInfo goes to the bottom of the init function and re-breakpoints it on the alloc line

At this point we see the objc_alloc method called, so keep going

We also found _objc_rootAllocWithZone and obj_msgSend.

Method -2. We debug through assembly, and we can also quickly locate objc_alloc methods

Method -3. Add a sign breakpoint to alloc by adding a sign breakpoint

The second step is to trace the alloc flow

Method master, we began to explore, home page let’s run the code, look at the print results

**2021-09-01 10:27:27.374802+0800 001-alloc&init explore [368:122896] <LGPerson: 0x283f04000> -0x283f04000-0x16ced1C08 ** **2021-09-01 10:27:33.671104+0800 001-alloc&init explore [368:122896] <LGPerson: 0x283f04000> -0x283f04000-0x16ced1C00 ** **2021-09-01 10:28:27.939738+0800 001-alloc&init Explore [368:122896] <LGPerson: 0x283f04000>-0x283f04000-0x16ced1bf8**Copy the code

Conclusion: object addresses in p1, P2, p3 memory are the same, but pointer addresses are completely different

To expand:

1. Pointer exists in stack stack area, object exists in heap heap area 2. Stack area is applied by the system, open address from large to small, heap area is applied by the programmer, open address from small to large. In addition, we also have static variable area, constant string area, code area 4. We open the object memory address, in fact, is virtual memory address, not real memory address, we have no way to know the real memory address.

Ok, we are going to start tracing the alloc process, we are now located that alloc will call objC_alloc method, so we are going to read the source code [objC4-818.2], Apple open quite a lot of source code, for our study is very helpful, we should go to see the source code, learn the idea of god.

CallAlloc method

CallAlloc (Class CLS, bool checkNil, bool allocWithZone=false) {#if __OBJC2__ if (slowpath(checkNil &&! cls)) return nil; if (fastpath(! cls->ISA()->hasCustomAWZ())) { return _objc_rootAllocWithZone(cls, nil); } # shortcuts available. If (allocWithZone) {return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil); } return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc)); }Copy the code

_class_createInstanceFromZone see

_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); //2. Open space} 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); }Copy the code

How about instanceSize(extraBytes)

The inline size_t instanceSize (size_t extraBytes const) {/ / fast computational memory size if (fastpath (cache. HasFastInstanceSize (extraBytes))) { return cache.fastInstanceSize(extraBytes); Size_t size = alignedInstanceSize() + extraBytes; // CF requires all objects to be at least 16 bytes. // Minimum return 16 bytes if (size < 16) size = 16; return size; }Copy the code

Digging deeper, we found this method, after calculating the size of the object, we did a 16-byte alignment.

static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}
Copy the code

So once we’ve opened up memory, we’re going to go down but there’s a problem that we’re going to print the address of obj in a different format than we printed the address of P1 and p2, so we’re going to go down and go to initInstanceIsa

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

When we find that the initIsa method is responsible for initialization, we continue to follow through and find an ISA_t, which isa union with properties that are mutually exclusive

union isa_t { isa_t() { } isa_t(uintptr_t value) : bits(value) { } uintptr_t bits; private: // Accessing the class requires custom ptrauth operations, so // force clients to go through setClass/getClass by making this // private. Class cls; Struct {ISA_BITFIELD; struct {ISA_BITFIELD; // defined in isa.h }; bool isDeallocating() { return extra_rc == 0 && has_sidetable_rc == 0; } void setDeallocating() { extra_rc = 0; has_sidetable_rc = 0; } #endif void setClass(Class cls, objc_object *obj); Class getClass(bool authenticated); Class getDecodedClass(bool authenticated); };Copy the code

We see a macro ISA_BITFIELD, isa_t stores more important information, let’s take a look

# define ISA_BITFIELD \ uintptr_t nonpointer : 1; \ uintptr_t has_assoc : 1; \ uintptr_t has_cxx_dtor : 1; \ uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7ffffFE00000 */ \ uintptr_t weakly_referenced : 1; Uintptr_t unused: 1; \ uintptr_t has_sidetable_rc : 1; \ Uintptr_t extra_rc: 8 counterCopy the code

And then it returns the obj object. The process of a call is basically complete. In this process, we encountered a problem. Through the source code breakpoint, we found that the object’s alloc method is going, this part is some optimization of LLVM, apple’s purpose should be to mark and monitor memory requests.

static void fixupMessageRef(message_ref_t *msg) { msg->sel = sel_registerName((const char *)msg->sel); if (msg->imp == &objc_msgSend_fixup) { if (msg->sel == @selector(alloc)) { msg->imp = (IMP)&objc_alloc; } else if (msg->sel == @selector(allocWithZone:)) { msg->imp = (IMP)&objc_allocWithZone; } else if (msg->sel == @selector(retain)) { msg->imp = (IMP)&objc_retain; } else if (msg->sel == @selector(release)) { msg->imp = (IMP)&objc_release; } else if (msg->sel == @selector(autorelease)) { msg->imp = (IMP)&objc_autorelease; } else { msg->imp = &objc_msgSend_fixedup; }}Copy the code

Step 3 Memory analysis (object size space)

LGPerson *person = [LGPerson alloc]; person.name = @"stone"; person.nickName = @"ios"; NSLog(@" memory sizeof object type --%lu",sizeof(person)); NSLog(@" actual memory size of object --%lu",class_getInstanceSize([person class])); NSLog(@" system allocated memory size --%lu",malloc_size((__bridge const void *)(person))); 2021-09-01 16:04:36.477765+0800 001- Memory Alignment [404:180545] Object type Memory size --8 2021-09-01 16:04:36.478278+0800 001- Memory Alignment Rule [404:180545] Actual memory size of the object --24 2021-09-01 16:04:36.478312+0800 001- Memory alignment Rule [404:180545] Allocated memory size --32Copy the code

Conclusion: 1. Sizeof is the sizeof the return type. For example, double is 8 bytes, int4 bytes, void(*) is 8 bytes. Class_getInstanceSize Dynamically calculates the size of the object. Person 24 bytes Isa 8 bytes name 8 bytes nickName 8 bytes 3. Malloc_size Specifies the memory space allocated by the system. Aligned at 16 bytes, 32 bytes of space are needed to store 24 bytes of Person

Memory usage of different types

To be continued:

Step 4 Object nature nonPoiterIsa