preface

This is the second day of my participation in Gwen Challenge

This paper mainly focuses on the two points encountered in the process of exploring alloc for further study.

Attached is a link to the previous article: iOS Low-level Exploration alloc(1)

Memory alignment

Three principles of memory alignment

1: Data member alignment rules: The data member of a struct (or union), the first data member is placed at offset 0, and the starting position of each data member is from the size of the member or the size of the member’s children (as long as the member has children, such as arrays). Structure, etc.) (for example, if int is 4 bytes, it is stored from the address that is a multiple of 4.

2: Struct as members: If a structure has some struct members, then the struct members are stored from an integer multiple of the size of the largest element in the structure.

The total sizeof the structure, the result of sizeof, must be an integer multiple of its largest internal member. What is lacking must be made up.

validation

Define two structures below, use three principles to calculate the size of their memory, and then compare the printed results.

    struct Colors1 {
	char yellow; //1 byte offset: 0, to 0
	double red; //8 bytes, offset: 8, to 15
	int blue;  // offset: 16 to 19
    };  // The total size is 20 bytes, the maximum memory size is 8 bytes, and the complement size is 24 bytes

    struct Colors2 {
            char yellow; //1 byte offset: 0, to 0
            int blue; // Offset: 4 to 7
            double red; //8 bytes, offset: 8, to 15
    }; // The maximum memory size is 8 bytes, which is exactly a multiple of 8, so it is 16

    struct Colors3 {
            char yellow; //1 byte offset: 0, to 0
            double red; //8 bytes, offset: 8, to 15
            int blue;  // Offset: 16 to 19
            struct Colors1 color; // The maximum size is double 8 bytes, offse 24 to 48
    }; // The maximum memory size is 8 bytes, which is exactly a multiple of 8, so 48
    
    
    struct Colors1 color1;
    struct Colors2 color2;
    struct Colors3 color3;
    NSLog(@"%lu",sizeof(color1));
    NSLog(@"%lu",sizeof(color2));
    NSLog(@"%lu",sizeof(color3));
Copy the code

Attached is the color1 memory arrangement map,

The printed result is the same as above. Attached is a table of common data types in bytes

Why memory alignment

  • Performance reasons

Cpus read memory granularity in blocks, which can be 2, 4, 8, or 16 bytes. The block size is called Memory Access granularity.

If color2 is not memory aligned, the attributes are arranged in the content as shown,

If at this timecpuThe memory read granularity is4whencpureadblue, read [0-3], then read [4-7], then offset by [0,3], plus the first byte of [4-7]blueThe value of theta is greatly reducedcpuPerformance. Memory alignment is also a typical space-for-time optimization.

  • There are also some reasons for platform transplantation, there is not too much research here, interested students can look up information to understand.

Why does alloc call objc_alloc first

If you read the last article, you know that objc_alloc is called first in the alloc process, and you’re curious why.

The first guess is that the runtime did something, searched globally for the objc_alloc keyword, and finally found this method, and it looks like the method did something, ran a breakpoint, and it didn’t run there.

static void 
fixupMessageRef(message_ref_t *msg)
{    
    msg->sel = sel_registerName((const char *)msg->sel);

    if (msg->imp == &objc_msgSend_fixup) { 
        if (msg->sel == @selector(alloc)) {
            msg->imp = (IMP)&objc_alloc;
        } else if (msg->sel == @selector(allocWithZone:)) {
            msg->imp = (IMP)&objc_allocWithZone;
        } else if (msg->sel == @selector(retain)) {
            msg->imp = (IMP)&objc_retain;
        } else if (msg->sel == @selector(release)) {
            msg->imp = (IMP)&objc_release;
        } else if (msg->sel == @selector(autorelease)) {
            msg->imp = (IMP)&objc_autorelease;
        } else{ msg->imp = &objc_msgSend_fixedup; }}else if (msg->imp == &objc_msgSendSuper2_fixup) { 
        msg->imp = &objc_msgSendSuper2_fixedup;
    } 
    else if (msg->imp == &objc_msgSend_stret_fixup) { 
        msg->imp = &objc_msgSend_stret_fixedup;
    } 
    else if (msg->imp == &objc_msgSendSuper2_stret_fixup) { 
        msg->imp = &objc_msgSendSuper2_stret_fixedup;
    } 
#if defined(__i386__)  ||  defined(__x86_64__)
    else if (msg->imp == &objc_msgSend_fpret_fixup) { 
        msg->imp = &objc_msgSend_fpret_fixedup;
    } 
#endif
#if defined(__x86_64__)
    else if (msg->imp == &objc_msgSend_fp2ret_fixup) { 
        msg->imp = &objc_msgSend_fp2ret_fixedup;
    } 
#endif
}
Copy the code

So maybe I did something at compile time, so when I compiled “main.m” into “IR” file, I found that the original alloc was changed to objc_alloc, so LLVM did compile at compile time, Alloc [[xyz alloc] init] allocWithZone. But it’s hard to fathom why Apple is doing this because of its limitations.

Clang-s - fobjc-arc-emit - LLVM main.m