Alloc explore

What can be learned from this article:

  • How does alloc open up memory, and how much memory does it open up?
  • What is the relationship between memory and Pointers during alloc?
  • How does alloc open up memory space?
  • How to explore the underlying source code? One, two, three
  • How to get the underlying source, such as (Objc4/)
  • A detailed analysis of alloc source code
  • Alloc Load flow chart
  • What are compiler optimizations in different modes at the assembly level?
  • What is byte alignment? The benefits of byte alignment?
Start with the startup process:

The loading process

From _dyLD_start to dyld::main to dyLD initialzemainexecutable, ImageLoader::*, etc. To get ready for startup, such as main, loading dynamic libraries, shared memory, destructing global C++ functions, and a series of initializations, registration of callbacks are done in this step. This is not a detailed description of this article, only to introduce features. The red part is the beginning of the object loading process. After starting a series of functions from the App, go to libSystem_initializer -> libDispatch_init -> Preparing for the GCD environment -> _objc_init

Initialization of the OC object

1. How is oc object created? How do we alloc, init, and new? 3, in this process, what is the relationship between memory and pointer?

Why does P1 print the same as P2?

LGPerson *p1 = [LGPerson alloc];
Copy the code

Come to the conclusion:

  • P1 now has memory
  • P1 has a pointer to it
LGPerson *p2 = [p1 init];
LGPerson *p3 = [p1 init];
Copy the code

Since the print object P2 =p3, we conclude that:

  • P2 and P3 point to the same memory address
  • Init does nothing to the pointer

*p1 * P2 *p3 represents 3 pointer addresses that point to the same memory space at the same time. In the figure above, memory address 0x7FFEEDe340A8 0x7FFEEDe340A0 0x7FfEEDe34098 is 0x7FFEEDe34098.

  • *p1, *p2, and *p3 belong to memory addresses on the stack

  • *p1, * P2, *p3 are contiguous address Spaces separated by 8 bytes (explanation: 0x98+0x8= 0xA0, 0xA0 +0x8= 0xA8)

Details of the graphics:The relationship between memory and Pointers

Key point: continuous development, pointing to the same space

How does alloc do that? Is it true that init does nothing?

How to explore source code:

A:

  • Real machine model

Step 1: In project LGPerson *p1 = [LGPerson alloc]; Set a breakpoint at

Step 2: Run the project, stop after the breakpoint, and holdcontrol + Step intoGo to assembly code

You see the objc_alloc method, you see the familiar code, you get excited, you hold down Control + Step into again

As a result, valid information can no longer be seen because of Apple’s restrictions in real machine mode

  • Simulator mode

Step 1: In project LGPerson *p1 = [LGPerson alloc]; Step 2: Run the project. Stop after the breakpoint and holdcontrol + Step intoGo to assembly code

Step 3: Add a symbol breakpoint to objc_alloc as follows:

Step 4: Continue holdingcontrol + Step intoCome on down

So you see libobjc.a. dylib objc_alloc, you see the method that’s going to be called next, _objc_rootAllocWithZone,objc_msgSend, and then you finally find the underlying source code for objc_alloc, which dynamic library it comes from, More clues to explore down!

Method two: through the assembly process to view: the first step, set the project mode, menu bar Debug->Debug WrokFlow ->Always Show Disassembly, will run the project

Step 2: Now the break point is broken at LGPerson, press and holdcontrol + Step intoTo findobjc_alloc

Step 3: Set a symbol breakpoint:

Step 4: Hold againcontrol + Step intodebuggingobjc_alloc

So you see libobjc.a. dylib objc_alloc, you see the method that’s going to be called next, _objc_rootAllocWithZone,objc_msgSend, and then you finally find the underlying source code for objc_alloc, which dynamic library it comes from, More clues to explore down!

Third: directly through the known symbol breakpoint setting, directly into, usually used in conjunction with the second

Where is the underlying source?

Apple kaiyuan source summary: https://opensource.apple.com/

[Source Browser:opensource.apple.com/tarballs/]

The source code I’m looking at here isObjc4-818.2. Tar. Gz.From LGCocci teachers, the most beautiful man: https://github.com/LGCooci/objc4_debug, the need for partners can be acquired by themselves, quality three

Alloc source code analysis:

Start by opening the source projectObjc4-818.2 -Search,allocTo see the detailed flow of alloc source execution:

1, enter the_objc_rootAllocmethods

2, entercallAllocmethods

3. Here it is#if __OBJC2__Judgment,How to verify which method to goEnter the_objc_rootAllocWithZone

4, enter_class_createInstanceFromZonemethods

Alloc Load flow chart

Compiler optimization

Go to BuildSetting and find Optimization Level (GCC_OPTIMIZATION_LEVEL), which specifies the degree to which the generated code is optimized for speed and binary size

Set up the parameter
None[-O0] The compiler does not optimize the code. The goal of the compiler is to make the cost of compiling and Debug produce the desired results, usually used in Debug mode.
Fast[-O,O1] Fast, optimized compilers take longer to compile, and require more memory for large functions. The compiler tries to reduce code size and execution time without performing any optimizations that require significant compilation time.
Faster[-O2] Faster, the compiler performs almost all supported optimizations that do not involve spatial speed trade-offs. With this setting, the compiler does not perform loop unwinding or function inlining or register naming, which increases compilation time and the performance of the generated code.
Fastest[-O3] Sets all optimizations specified and turns on function inlining and register renaming options. This setting may produce larger binaries
The Fastest, Smallest [- Os] Fastest, smallest, this setting enables all faster optimizations that generally do not increase the code size, and it also does further optimizations that reduce the code size

Try writing a small example with different optimizations to verify compiler optimizations:

#import <UIKit/ uikit. h> #import "appdelegate. h" //MARK: -test function int lgSum(int a, int b){return a+b; } int main(int argc, char * argv[]) { int a = 10; int b = 20; int c = lgSum(a, b); NSLog(@" check compiler optimizations :%d",c); return 0; }Copy the code
  • None[-O0]

Execution result: Without optimization, all information is displayed completely in the register. I printed a, B, calculated money and calculated X0 register respectively. The result is as follows:Copy the code

  • The Fastest, Smallest [- Os]

Result: a and b variables are optimized, and even lgSum is optimized, leaving only one result 0x1e in register w8.

(Fastest, Smallest[-os] optimization scheme is selected, which leads to the loss of lgSum function. Similarly, callAlloc function is the same.)

What did Alloc do?

The source code parsing

How is alloc memory alloc? How much memory alloc

The instanceSize function determines the size of the object’s memory. The instanceSize function determines the size of the object’s memory. The instanceSize function determines the size of the object’s memory. So if you don’t have ignedInstancesize in there, we’ll execute word_align, Data ()->ro()->instanceSize ()->instanceSize ()

By default, we don’t create any member variables, so the memory space that the class opens up is 8 bytes, because we inherited from NSObject, and NSObject has a member variable isa, which is 8 bytes because isa isa pointer to a structure, so we create a new object with no member variables, and the default memory size is 8 bytes

// Object struct class_ro_t {uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; #ifdef __LP64__ uint32_t reserved; Endif //_class_createInstanceFromZone size = CLS ->instanceSize(extraBytes); // May be unaligned depending on class's ivars. uint32_t unalignedInstanceStart() const { ASSERT(isRealized()); return data()->ro()->instanceStart; } // Class's instance start rounded up to a pointer-size boundary. // This is used for ARC layout bitmaps. uint32_t alignedInstanceStart() const { return word_align(unalignedInstanceStart()); } // uint32_t unalignedInstanceSize() const {ASSERT(isRealized()); // Uint32_t unalignedInstanceSize() const {ASSERT(isRealized()); return data()->ro()->instanceSize; } // The class ivar size is rounded up to the pointer size boundary. uint32_t alignedInstanceSize() const { return word_align(unalignedInstanceSize()); } 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

Byte alignment

Advantages of byte alignment: Space for time

  • The 8 bytes come from the pointer to the ISA structure of the NSObject
  • Less than 16 is 16
  • If it is greater than 16, it will depend on the memory distribution of the object (take an integer multiple of x based on the x passed in). If it is passed in 8, it will end up with a multiple of 8
#ifdef __LP64__ # define WORD_SHIFT 3UL # define WORD_MASK 7UL # define WORD_BITS 64 #else # define WORD_SHIFT 2UL # Define WORD_MASK 3UL # define WORD_BITS 32 #endif # define WORD_MASK = 7 static inline uint32_t word_align(uint32_t x) { return (x + WORD_MASK) & ~WORD_MASK; }Copy the code

Engineering commissioning

1, verify that the code executes the #if __OBJC2__ judge function

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); } #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

Solution:

Add symbolic breakpoints to the _objc_rootAlloc, callAlloc, _objc_rootAllocWithZone methods, and get the project running

We stop at the _objc_rootAlloc method as expected, register read, but we don’t find the LGPerson class, because LGPerson hasn’t been initialized yet, so the solution is to put the break point in the past and let the system method finish executing. Wait until you get to LGPerson

The result is that _objc_rootAllocWithZone will be executed first, followed by objc_msgSend, which proves that #if __OBJC2__ is true and that the internal code is executed. But if you look carefully, the function being executed is _objc_rootAlloc, not callAlloc in the source code. Why?

* Creation is not easy, reproduced please indicate the source, thank you!