Let’s look at an example firstAnalyze the difference between the memory address and pointer address of the three variables;

The above outputs are respectivelyContent, memory address, and pointer addressThe output is: Conclusion:In the figure above, we can see three objectsPoint to the same memory spaceSo theirIdentical in content and memory address, but the pointer address of their object isdifferent

P1, P2, and P3 are the same, but their addresses are different, indicating that different Pointers point to the same memory space

%p -> &p1: is the pointer address of the object, %p -> p1: is the memory address to which the object pointer pointsCopy the code

That’s what this quest is all about. What does alloc do? What does init do?

To prepare data

  • Download objC4-781 source code
  • Compile source code, can refer to iOS- underlying principle 03: OBJC4-781 source code compile & debug

## objC source code exploration process

  • [First step] First enter according to the alloc method of the Person class in main functionallocMethod source code implementation (that is, source code analysis begins),
+ (id) alloc
{
    return  _objc_rootAlloc(self);
}
Copy the code
  • [Step 2] Jump to_objc_rootAllocSource code implementation
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
Copy the code
  • [Step 3] Jump tocallAllocSource code implementation
static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, Bool allocWithZone=false) {#if __OBJC2__ https://www.jianshu.com/p/536824702ab6 * / / / checkNil to false,! CLS is also false, so slowpath is false, and false checks don't go inside if, that is, they don't return nil if (slowpath(checkNil &&! cls)) return nil; // Determine if a class has a custom +allocWithZone implementation. If not, go to the implementation inside 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

As shown above, in the Calloc method, when we are not sure where the implementation is going, we can use breakpoint debugging to determine which part of logic to execute. This is done to _objc_rootAllocWithZone

slowpath & fastpath

Slowpath and FastPath are both macros defined in objC source code as follows

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

The __builtin_expect directive was introduced by GCC. 1. Purpose: The compiler can optimize the code to reduce the performance degradation caused by instruction jumps. Function: Allows the programmer to tell the compiler which branch is most likely to be executed. __builtin_expect(EXP, N) That means that e to the N is very likely. 4. The fastPath definition __builtin_expect((x),1) indicates that the value of x is more likely to be true; Slowpath’s __builtin_expect((x),0) means that x is more likely to be false. 6, in daily development, can also be set to optimize the compiler, to achieve the purpose of performance optimization, set path as: Build Setting –> Optimization Level –> Debug –> change None to fastest or smallest

cls->ISA()->hasCustomAWZ()

Fastpath CLS ->ISA()->hasCustomAWZ() specifies whether a class has a custom +allocWithZone implementation. That is, go to _objc_rootAllocWithZone

  • Step 4: Jump to_objc_rootAllocWithZoneSource code implementation
id _objc_rootAllocWithZone(Class cls, // allocWithZone ignores the value of the hide Return _class_createInstanceFromZone(CLS, 0, nil, OBJECT_CONSTRUCT_CALL_BADALLOC); }Copy the code
  • Step 5: Jump to_class_createInstanceFromZoneThis part is the core operation of alloc source code. It can be seen from the following flow chart and source code that the implementation of this method is mainly divided into three parts cls->instanceSize: calculateNeed to open upMemory size • calloc: Applies for memory, returns address pointer •obj->initInstanceIsa: Associates the class with isa
static ALWAYS_INLINE id _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(extrabize); 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); } if (slowpath(! obj)) { if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) { return _objc_callBadAllocHandler(cls); } return nil; } if (! 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_getInstanceSize([NSObject class]) returns the memory required by the object

•** malloc_size** The actual memory space allocated by the system

• Sizeof Specifies the number of bytes of the data type. Sizeof is an operator that is determined by editing

According to the source code analysis, the realization flow chart is as follows:

####alloc core operations Core operations are located in calloc methods

##### CLS ->instanceSize: Calculate the required memory size The process for calculating the required memory size is as follows• 1. Jump toinstanceSizeSource code implementation

Const size_t instanceSize (size_t extraBytes) {/ / fast calculation memory size if the compiler (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; // CF requires all objects be at least 16 bytes. return size; }Copy the code

FastInstanceSize (); fastInstanceSize (); fastInstanceSize ()

size_t fastInstanceSize(size_t extra) const { ASSERT(hasFastInstanceSize(extra)); //Gcc's built-in function __builtin_constant_p is used to determine if a value is compile-time. If EXP is constant, the function returns 1. Otherwise return 0 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 // Delete FAST_CACHE_ALLOC_DELTA16 8 bytes added by setFastInstanceSize return align16(size + extra-fast_cache_alloc_delta16); }}Copy the code

• 3. Jump to the source code implementation of ALIGN16, which is the 16-byte alignment algorithm

// 16-byte alignment algorithm static inline size_t align16(size_t x) {return (x + size_t(15)) & ~size_t(15); }Copy the code

Memory byte alignment principles

Before explaining why 16-byte alignment is needed, it is important to understand the principles of memory byte alignment

• Data member alignment rules: Struct or union data member, the first data member is placed at offset 0, and the starting position of each data member is an integer multiple of the size of the member or its children (as long as the member has children, such as data, structure, etc.) (for example, int is 4 bytes in 32-bit machines, • Data members are structures: If a structure has some structure members, the structure members are stored from an integer multiple of the size of the largest element in the structure (for example: Char, int, double (struct A, struct B, char, int, double); The total sizeof a structure, the result of sizeof, must be an integer multiple of its internal enlarged members

Byte alignment is required for several reasons:

• Memory is usually composed of bytes. When the CPU accesses and writes data, it does not store data in bytes, but in blocks. The size of blocks is the strength of memory access. Frequently accessed data bytes unaligned, would greatly reduce the performance of the CPU, so can reduce the access number to reduce the overhead of CPU, 16 byte alignment, is because in an object, the first attribute isa account for 8 bytes, an object, of course, there must be other attributes, when no attributes, 8 bytes, 16 byte alignment, If not reserved, the isa of this object and the ISA of other objects are next to each other, which can easily cause access chaos. • 16-byte alignment can speed up CPU reads while making access more secure without causing access chaos

• In byte alignment algorithms, alignment is primarilyobjectAnd the essence of an object is astruct objc_objectThe structure of, •The structure of the bodyIn memoryContinuous deposit, so you can use this to perform strong rotation on the structure. • Apple’s early days were8Byte alignment, now16Byte alignment The following uses align (8) as an example to illustrate the calculation process of the 16-byte alignment algorithm, as shown below• First place the original memory 8size_t(15)So if you add them up, you get 8 plus 15 is equal to 23 times omegasize_t(15)The 15 were~ (take the inverse)The rule for the operation, ~ (invert) is:1 becomes 0,0 becomes 1• Finally, take the inverse result of 23 and 15& (and)The rules for the operation, & (and) are:1 for both, 0 for both, the final result is 16, that is, the size of memory is16Is a multiple of

####calloc: applies for memory, returns address pointer The memory size calculated by instanceSize, applies for memory size from memory, and assigns the value to obj. Therefore, obj is a pointer to the memory address

obj = (id)calloc(1, size);
Copy the code

Po obj is nil when calloc is not executed. Po obj returns a hexadecimal address 0x 123456f. The usual print format for an object is

(is a pointer). Why not here?

This is mainly because the objC address has not been associated with the CLS passed in, and confirms that alloc’s primary purpose is to open up memory

####obj->initInstanceIsa: class with ISA association ####obj->initInstanceIsa: class with ISA association ####obj->initInstanceIsa: class with ISA associationThe main process is to initialize an ISA pointer to the requested memory address, and then associate the pointer with the CLS class

You can also use breakpoint debugging to verify the above statement. After initInstanceIsa is executed, a summary of the object pointer

can be obtained through Po obj

• Through the analysis of the source code of alloc, it can be learned that the main purpose of alloc is to open up memory, and the opened memory needs to use the 16-byte alignment algorithm, and the size of the opened memory is basically an integer multiple of 16 • The core steps of open up memory are three steps: calculation, application and association

Init source code exploration

After exploring the source code of alloc, next explore the source code of init. Through the source code, we know that the source code of INTI has the following two kinds

Methods the init

+ (id)init {
    return (id)self;
}

Copy the code

Init here is a constructor that is designed through a factory (factory method pattern), primarily to provide the constructor entry to the user. The main reason why you can use id strong here is that once the memory bytes are aligned, you can use type strong to convert them to the type you want

Instance method init

  • Explore the example method init with the following code
LGPerson *objc = [[LGPerson alloc] init];

Copy the code
  • throughmainIn theinitJump to the source implementation of init
- (id)init {
    return _objc_rootInit(self);
}

Copy the code
  • Jump to_objc_rootInitSource code implementation
id
_objc_rootInit(id obj)
{
    // In practice, it will be hard to rely on this function.
    // Many classes do not properly chain -init calls.
    return obj;
}

Copy the code

With the above code, you can return self as passed in.

New source code exploration

In addition to init, you can also use new in development. There is no difference in nature between the two. The following is the source code implementation of new in objC. So you can conclude that new is actually equivalent to alloc init

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

Copy the code

However, it is not recommended to use new in general development, mainly because sometimes the init method will be overridden to do some custom operations, such as initWithXXX, which will call [super init] in this method, and initialization with new may not go to the custom initWithXXX part.

For example, in CJLPerson, there are two initialization methods, the overridden parent init and the custom initWithXXX method, as shown in the figure below

  • When initializing with alloc + init, the following is printed

  • When initializing with new, the print looks like this

conclusion

  • If a subclass has not overridden its parent class’s init, new calls the parent class’s init method
  • If a subclass overrides its parent’s init, new calls the init method that the subclass overrides
  • If you use a custom alloc + init, you’ll be able to customize the initialization, pass in some of the parameters of the subclass, and you’ll end up in the parent class init, which is much more extensible and flexible than new.

Note: The compiled objC-781 source code can be downloaded at Github

supplement

[QUESTION] Why can’t I break itobj->initInstanceIsa(cls, hasCxxDtor);?

This is mainly because breakpoints stop the flow of custom classes not at the system level