This is the first day of my participation in Gwen Challenge

Having written OC code for years, Alloc is an old friend, and we see him all the time when we’re creating objects, but we never really explore what’s going on behind the scenes. Today, we’re going to find out.

Assuming that

Before exploring, let’s assume that if you were to implement alloc for memory allocation, how would we think about this problem? I think there are three aspects:

  • To whom?
  • For how much?
  • How to give?

We know that creating objects is actually allocating memory. These three aspects of memory allocation correspond to the following three key operations:

  • To whom? >> After the allocation, the marker block is associated with the class.
  • For how much? >> How much memory to allocate (to allocate, you have to know how much?
  • How to give? >> Assign

Verification and exploration

So how do we test our hypothesis? As the saying goes: Talk is cheap, show me the code, let’s find the corresponding source code in objC.

Wait, the problem is again, there are so many sources, how do you know which part to look at? Here’s a little trick:

Debugging technique

Insert a Breakpoint on the alloc line and execute it. Symbolic Breakpoint Symbolic Breakpoint is created when the Breakpoint is broken. The symbol field is alloc and the program continues to break in an assembly code interface.

Ok, so we know we’re looking for the libobJC related code.

The preparatory work

  1. Let’s download the source code from ObjC4-818.2.

Alloc source code exploration

The source code is tracked below, and the flow chart is shown first, starting with alloc method, and the following execution process is roughly as shown in the figure:

The _class_createInstanceFromZone method splits into three methods:

  • CLS ->instanceSize: calculates the memory size
  • (id)calloc(1, size) : open memory, return address pointer
  • Obj ->initInstanceIsa: initializes the pointer associated with the class

They correspond to the three actions we described at the beginning of this article.

In this case, _class_createInstanceFromZone is the core of the analysis, and the three diverging methods are the three core methods.

Ok, no hurry, let’s go through the process of _class_createInstanceFromZone.

Peripheral method

The first entrance

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

Enter the _objc_rootAlloc

id _objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
Copy the code

Enter the callAlloc

static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, Bool allocWithZone=false) {#if __OBJC2__ // slowPath (x):x is likely to be false, Slowpath (x):x is likely to be true // FastPath (x):x is likely to be true // In fact, removing fastPath and slowPath does not affect any functionality at all. cls)) return nil; If (fastPath (!) {// If (fastPath (!); cls->ISA()->hasCustomAWZ())) { return _objc_rootAllocWithZone(cls, nil); } #endif // No 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

Slowpath and FastPath macros are defined as follows:

#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))
Copy the code

__builtin_expect plays the role of optimizing performance, indicating that the first parameter has a high probability of being the second parameter when branching prediction, so fastpath (x) means that X has a high probability of being true, and slowpath(x) means that X has a high probability of being false, and the return value is X itself. This instruction was introduced by GCC to allow the programmer to tell the compiler which branch is most likely to be executed. This command is written as __builtin_expect(EXP, N). The probability that e to the N is equal to N is high.

Enter the _objc_rootAllocWithZone

id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
    // allocWithZone under __OBJC2__ ignores the zone parameter
    return _class_createInstanceFromZone(cls, 0, nil,
                                         OBJECT_CONSTRUCT_CALL_BADALLOC);
}
Copy the code

Core method

Now we’re finally at _class_createInstanceFromZone.

CLS ->instanceSize

  • How much memory do I need?
  • Actual allocated memory (memory alignment operation for efficient CPU processing)

Why is 16-byte alignment needed

  • The CPU reads data in fixed byte blocks, so there is no need to frequently change the length of each byte read. If the frequent reading of unaligned bytes reduces CPU performance and read speed, it is a way to trade space for time.
  • Safer Since isa Pointers are eight bytes in an object, without section alignment, objects will be next to each other, causing access confusion. 16-byte alignment, which leaves some space for more secure access

(id)calloc(1, size)

< class name: 0x100726d00> < class name: 0x100726D00 > < class name: 0x100726d00> < class name: 0x100726d00>

  • Obj has no associative binding with CLS
  • It also verifies that Calloc only opens up memory

The malloc function cannot initialize the allocated memory space. Calloc () initializes every bit of the allocated memory space to zero. Malloc and calloc are used to dynamically allocate memory, while realloc is used to expand or shrink memory pointed to by a given pointer.

Obj ->initInstanceIsa

Enter the initInstanceIsa

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

Now that the isa pointer is initialized, we can print objc and find that the class name is already available, indicating that the class has been associated with it.

The role of new:

We know that another common way to create objects, besides alloc, is to use the new method. Here is the implementation of the new method:

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

So new is basically alloc and init. The downside of this is that when we call the new method, we don’t go through the custom init method, we go directly through the NSObject init method, so it’s not recommended for development.

conclusion

At this point, our alloc exploration is complete.

The core of alloc is allocating memory, and allocating memory involves three key operations:

  • How much memory to allocate
  • allocated
  • After allocation, the marker block is associated with the class