Writing in the front
Today’s iOS market environment is different from the past. When you go out for an interview, the questions asked by the interviewer are no longer just at the UI level, but at the bottom level to test the ability of the interviewer. At every turn is left a bottom, another bottom of the ask, most people are a question, very meng force! Interviews often end in failure, so today we’re going to take a look at the bottom line.
The interface of each of our programs basically starts by instantiating an object, alloc, init. So what exactly is going on at the bottom of OC? Today we will take a good look at the underlying alloc workflow.
1. Throw out a brick to attract jade
Take a look at the following code first
JPStudent *s1 = [JPStudent alloc];
JPStudent *s2 = [s1 init];
JPStudent *s3 = [s1 init];
NSLog(@"%@-%p-%p",s1,s1,&s1);
NSLog(@"%@-%p-%p",s2,s2,&s2);
NSLog(@"%@-%p-%p",s3,s3,&s3);
Copy the code
Print the result
<JPStudent: 0x600002d583e0>-0x600002d583e0-0x7ffee6343098
<JPStudent: 0x600002d583e0>-0x600002d583e0-0x7ffee6343090
<JPStudent: 0x600002d583e0>-0x600002d583e0-0x7ffee6343088
Copy the code
Based on the results of the printout,s1/s2/s3
The address of the object is the same, but the address of the pointer is different. becausealloc
Open up a block of memory whileinit
The constructor of this class does not open up memory, so all three objects refer to the same memory address space. To make it easier for you to understand, I drew the following picture 👇
2. Get down to business
There are three ways of low-level debugging
- Breakpoint debug trace
- Symbolic breakpoints trace debugging
- Assembly tracking
2.1 Breakpoint Debugging
Place a breakpoint where the object is instantiated, wait for the breakpoint to come here, and then hold down the Control key + Step into to step
Step by step, you can see that our bottom layer is objc_alloc
2.2 Symbolic breakpoint
Step 1
Set a symbolic breakpoint
Step 2
Lower sign breakpoint
Running the program at this point will locate the corresponding symbol breakpoint
2.3 Assembly tracking
Step into assembly code
3. Start your source code exploration journey
We want to see the implementation of alloc, click in and walk to here, this is awkward, want to know more about it, but I don’t know how to start 😂, how to do this??
This is because the specific implementation method here is not exposed, we can go to apple’s open source website to see, find the open source, see if you can find the answer?
The Source code address is here 👉Apple Open Source, go to Source Browser here to see a more direct point, with the Source code we can go deeper to explore the bottom.
From the above screenshots on the official website, we can see that Apple has opened a lot of source code, Apple can still! There’s so much open source for apple developers to learn from. So which source should we find to explore the alloc workflow?
There are three ways to explore debugging at 👆 above, and we already know that objc_alloc is what we’re looking for, so let’s do a search on the open source interface
Right, in objC4
Here is the latest version of Apple, we should try to find the latest source code to explore learning.
Today we are going to talk about the version of 818.2, because MY computer has this version of the configuration compiled good engineering code, here will not introduce the source code configuration compiled process 😝, free will be a tutorial 😁.
So let’s officially start exploring alloc step by step!
Open source project, break point one by one with the process
Fastpath CLS ->ISA()->hasCustomAWZ(); fastPath CLS ->ISA()->hasCustomAWZ(); That is, go to _objc_rootAllocWithZone
Through the above trace debugging, the following flow chart of 👇alloc is obtained
_class_createInstanceFromZone is the core alloc process, so let’s take a look at it
_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;
// Open up 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;
}
// Associate objects
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
_class_createInstanceFromZone method execution flowchart
3.1 CLS – > instanceSize
The CLS ->instanceSize method is an if judgment and returns a value
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(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
Enter the cache.fastInstanceSize method to see what is returned.
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
returnalign16(size + extra - FAST_CACHE_ALLOC_DELTA16); }}Copy the code
The fastInstanceSize method calculates the size of the memory needed to create an object, aligns it with 16 bytes, and returns the calculation result. Finally, it calculates the size of the object, and assigns the size to obj via calloc, so obj is a pointer to the memory address.
3.2 calloc
The breakpoint is broken at obj = (id)calloc(1, size)
As shown above, the Po command in LLDB prints obj, which is nil, so step it down again
From the figure above we can see that a hexadecimal address is printed because the following 👇 code is executed
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
Copy the code
If calloc is not executed, Po obj will be nil or output a dirty address. After that, Po obj returns the address. But we found that this is not the same as we usually develop when printing ???? Shouldn’t it be
??
3.3 initInstanceIsa
Because the address of OBj is not associated with CLS, the object must be associated
// Associate object methods
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
The specific flow chart is as follows: 👇
An isa pointer to a memory address is initialized, and an ISA pointer to a memory address is associated with the CLS class. It is clear that the output is now the familiar print
4. To summarize
size = cls->instanceSize(extraBytes)
Calculate the size of memory required, using a 16-byte alignment algorithmcalloc
Request the required memory spaceinitInstanceIsa
To the object, and eventually return the instance object
Finally, a complete flow chart is attached for your understanding
🌹 please bookmark + follow, comment + forward, in case you can’t find me next time, ha ha 😁🌹
🌹 welcome everyone to leave a message exchange, criticism and correction, learn from each other 😁, improve themselves 🌹