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
- 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