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/s3The address of the object is the same, but the address of the pointer is different. becauseallocOpen up a block of memory whileinitThe 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

  1. Breakpoint debug trace
  2. Symbolic breakpoints trace debugging
  3. 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

  1. size = cls->instanceSize(extraBytes)Calculate the size of memory required, using a 16-byte alignment algorithm
  2. callocRequest the required memory space
  3. initInstanceIsaTo 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 🌹