IOS martial arts esoteric article summary

Writing in the front

In the iOS secret book ① : OC object principle – (alloc & init & new) this article describes the process of creating the underlying object, so this article will explore the object attributes in memory alignment and malloc source code analysis

A possible secret Demo for this section

1. The influence factors of object memory opening (Supplement)

From the previous article, we learned that the internal size = CLS ->instanceSize(extraBytes) of an object created through the _class_createInstanceFromZone method calculates the amount of memory required to create the object. So what are the factors that affect how objects open up memory?

Object attributes (more specifically, object member variables)

An 🌰 :

  • When ourTCJPersonWhen an object has no other attributes, it has only one parentNSObjectinheritedisaIs created at this timeTCJPersonThe size of memory required by the object is 16 bytes.

  • When we add onenameProperty, at this timesizeSize 16(if (size < 16) size = 16).

  • And then we’re adding onenickNameProperty, which is required at this timesizeSize 32 (object’s byte alignment is 16 bytes, the memory size of the open object must be a multiple of 16)

② How to view the display of object properties in memory

Test code:

Note: If the object is created with no assigned attribute — it will be a memory dummy address

We should assign values to the corresponding attributes first, otherwise they will be false addresses in memory. Because memory is contiguous, if it’s not used, it’s a wild pointer in memory.

②.1 The first method is the LLDB instruction — view the display of object properties in memory

LLDB debugging commands
  • powithp: p stands for “expression” — prints the object pointer; And Po is “expression-o” — prints the object itself

  • The x object represents the memory address of the object printed in hexadecimal notation (x represents memory read)

    Because iOS is in small-endian mode (high bytes of data are stored at high addresses in memory, and low bytes of data are stored at low addresses in memory — the data is stored in reverse), data is read backwards

  • X / 8 gx objectRepresents the output of 8 hexadecimal 8-byte address space (x for hexadecimal, 8 for 8, g for 8 byte units, equivalentX/eight xg object)

According to our computer foundation and LLDB instructions, it can be found

  • The first paragraph isisa(Starting with 64-bit,isaWe need to do a bit operation& ISA_MASKOperation, whereas on x86,ISA_MASKThe value of0x0000000ffffffff8ULL)
  • In the second paragraph0x00000012is18– the correspondingageAnd the62,61, respectively,b,atheASCIIcoding
  • In paragraph 3, the Po comes out to beTCJ– the correspondingname
  • In paragraph 4, the Po appears to beCJ– the correspondingnickName
  • The fifth paragraph is Po185– the correspondingheight
View console output:

Undeclare properties to view console output:

namelyTCJPersonClass does not declare any attributes:

Ask questions

Q1: Why are the order of member variables different from the order in which we declare attributes?

Q2: What are sizeof, class_getInstanceSize, and malloc_size? Later on…

Q3: Instead of saying that the object is at least 16 bytes, why does class_getInstanceSize output 8 bytes? Later on…

Debug->Debug Workflow->View Memory(Shift + Command +M)

The second method is generally not recommended.

Byte alignment

① sizeof, class_getInstanceSize, malloc_size

  • sizeof(): is an operator, not a function. The incomingThe data type, output memory size, inCompile timeDetermine. Is related only to the data type, not the specific value. (such as:bool2 bytes,int4 bytes, object (pointer)8 bytes
  • class_getInstanceSize: depends on the<objc/runtime.h>, it isruntimeTo provide theapiforGets the amount of memory used by instance objects of the classAnd returns the number of bytes specified by itThe essence is to get the memory size of the member variables in the instance object(8-byte alignment)
  • malloc_size: depends on the<malloc/malloc.h>To return toActual memory size allocated by the system(16-byte alignment)

The previous print is verified.

To sum up a wave:

  • Sizeof: calculates the sizeof memory used by a type, including basic data types, objects, and Pointers

    • For those similar tointWith basic data like that,sizeofThe size of memory used by the data type is not the same as that used by the data type
    • And for things likeNSObjectFor instance objects defined, the nature of their object types is onePointer to struct objc_object, sosizeof(objc)Print isThe size of the pointer to objcWe know that a pointer has 8 bytes of memory, sosizeof(objc)Print is 8. Note: 8 bytes here withisaPointers don’t matter at all!!
    • For Pointers,sizeofIt prints 8, because a pointer has 8 bytes of memory.
  • Class_getInstanceSize: Calculates the actual memory usage of the object. This varies depending on the class attributes. If a custom class has no custom attributes and just inherits from NSObject, the actual memory usage of the instance object of the class is 8, following 8-byte alignment.

  • Malloc_size: Calculates how much memory the object is actually allocated. This is done by the system. The preceding output shows that the actual allocated and occupied memory is not equal.

② Object memory alignment

We know that in terms of objects as a whole, Apple uses 16-byte alignment to open up memory size and improve system access performance.

What about the inside of the object?

  • The essence of an object is a structure, which we will discuss in more detail in a later chapter. Therefore, to study the internal memory of the object is to study the memory layout of the structure.
  • Memory alignment purpose: Maximize resource utilization.

③ Internal alignment of the structure

Do 🌰 to see:

Cjstruct1-24 cjstruct2-16 CJStruct3-32 CJstrucT4-24 is displayed.

The only difference is that the order in which the variables are defined is not the same. So why do they take up the same amount of memory? The ordering of elements within a structure affects memory size. This is the memory byte alignment phenomenon in iOS.

Each compiler on a particular platform has its own default “alignment coefficient” (also known as the alignment modulus). Programmers can change this by precompiling the command #pragma pack(n), n=1,2,4,8,16, where n is the “alignment factor” you want to specify. In iOS, Xcode defaults to #pragma Pack (8), which is 8-byte alignment

Note: The 8-byte alignment here is the internal alignment of the structure, and the actual space allocated by the object in the system follows the 16-byte alignment rule.

Three memory alignment rules:

  • The alignment rules for data members can be understood asmin(m, n)WheremRepresents the starting position of the current member,nRepresents the number of digits required for the current member. If the conditions are metmaliquotn(i.e.m % n == 0),nmLocation starts storage, otherwise continue checkingm+1Can be divided exactly byn, until it is divisible, thus determining the starting position of the current member.
  • Data members for structure: when the structure nested structure, as members of the data structure of its own length as members of the external structure of the biggest memory size (i.e., when determining the offset of compound type members as a whole is a compound type view), and from its internal structure members began to store integer times the size of the largest element address. For example, if b contains char, int, double, etc., then B should be stored at multiples of 8.
  • Finally, the memory size of the structure must be an integer multiple of the size of the maximum member memory in the structure.

Table of the number of bytes used by basic iOS data types

Analyze the previous 🌰 using the structure alignment rule

CJStruct1 memory size calculation:

  • Variable A: takes 8 bytes and starts at 0, when min (0, 8), i.e. 0-7, stores A
  • Variable B: 1 byte, starting from 8, min (8, 1), 8 can be divisible by 1, that is, 8 stores B
  • Variable C: 4 bytes, starting from 9, min (9, 4), 9 is not divisible into 4, continue moving forward until min (12, 4), starting from 12, i.e. 12-15 stores C
  • Variable D: takes 2 bytes and starts from 16. At this point, min(16, 2), 16 can be divisible by 2, that is, 16-17 stores D

Therefore, the memory size required by CJStruct1 is 18 bytes, and the maximum number of bytes of variable in CJStruct1 is 8, so the actual memory size of CJStruct1 must be an integer multiple of 8, rounded up to 24, mainly because 24 is an integer multiple of 8. So sizeof(CJStruct1) is 24.

CJStruct2 memory size calculation:

  • Variable A: takes 8 bytes and starts at 0, when min (0, 8), i.e. 0-7, stores B
  • Variable B: 4 bytes, starting from 8, min (8, 4), 8 can be divisible into 4, that is, 8-11 stores C
  • Variable C: occupies 1 byte, starting from 12, when min(12, 1), 12 can be divisible by 1, that is, 12 stores D
  • Variable D: takes 2 bytes and starts at 13. At this point, min (13, 2), which cannot be divisible by 2, continues to move forward until min (14, 2), which starts from 14 and stores C at 14-15

Therefore, the memory size required by CJStruct2 is 16 bytes, and the maximum number of bytes of variable in CJStruct2 is 8, so the actual memory size of CJStruct2 must be an integer multiple of 8, and 16 is exactly an integer multiple of 8. So sizeof CJStruct2 is 16.

CJStruct3 memory size calculation:

  • Variable E: occupies 4 bytes, starting from 0, when min (0, 4), i.e., 0-3 stores E
  • Structural memberCJStruct1:CJStruct1Is a structure that takes 24 bytes. According to the second memory alignment principles, structure members from its internal members of the size of the largest integer times began to storage, and the biggest size of 8, the members of CJStruct1 so CJStruct1 should begin from 8 integer times, current started with four, so do not conform to the requirements, the need to move back to the 8, 8 is integer times, in line with the memory alignment principles, So 8-31 stores CJStruct1.

Therefore, the memory size required by CJStruct3 is 32 bytes, and the maximum variable in CJStruct1 is CJStruct1, and the maximum memory size of its member is 8. According to the principle of memory alignment, the actual memory size of CJStruct3 must be an integer multiple of 8, and 32 is exactly an integer multiple of 8. So sizeof(CJStruct3) is 32.

CJStruct4 memory size calculation:

  • Variable E: takes 8 bytes and starts from 0, when min (0, 8), i.e. 0-7, stores E
  • Structural memberCJStruct2:CJStruct2Is a structure, 16 bytes. According to the memory alignment principle 2, structure members are stored from an integer multiple of the size of their largest internal member, whileCJStruct2The largest member in theCJStruct2We want to start with an integer multiple of 8, so we’re currently starting at 8, which is ok, which is memory alignment, so 8-23 stores CJStruct2.

Therefore, the memory size required by CJStruct4 is 24 bytes, and the maximum variable in CJStruct4 is CJStruct2, and the maximum memory size of its member is 8. According to the principle of memory alignment, the actual memory size of CJStruct4 must be an integer multiple of 8, and 24 is exactly an integer multiple of 8. So sizeof(CJStruct4) is 24.

④ Memory optimization (attribute rearrangement)

If the objects are allocated in the order in which they are declared by default, a lot of memory is wasted on 8-byte alignment of attributes, so the system rearranges the attributes of the objects to maximize our memory

Validation ①: In terms of CJStruct1 and CJStruct2, their member attributes are the same, but their attributes are arranged in different positions, and they occupy different memory respectively.

Validation ②: This is also demonstrated in the previous example of how to view object properties displayed in memory. We declare TCJPerson attributes in the following order: ISA (inherited NSObject) -> name(NSString) -> nickName(NSString) ->age(int) -> height(long) -> C1 (char) -> c2(char);

Isa (inherited NSObject) ->age(int) -> C2 (char) -> c1(char) -> nickName(NSString) -> name(NSString)-> Height (long), and stores age in a block with c1 and c2. This is where Apple’s memory optimization comes in.

5. Small eggs

  • Malloc_size reads multiples of 16 in order to prevent error tolerance by using memory aligned with 16 bytes, leaving enough space between objects to avoid out-of-boundary access.
  • But to avoid wasting too much memory, the system rearranges attributes within each object and uses 8-byte alignment to keep the resource usage of individual objects as small as possible (so class_getInstanceSize reads multiples of 8).

Third, malloc source auxiliary analysis

From the last article, we have learned that creating an object after_class_createInstanceFromZoneMethod when its internalobj = (id)calloc(1, size)The method is based on the calculated space sizesize(e.g.,size = 40), go to the system to apply for space, and return the address pointer. We found that clickcallocInside, you can only seecallocStatement. No further progress

We can see that the calloc declaration is in the malloc source code.

Open the compiled malloc source code that I have prepared for you.

1) calloc

inlibmallocNew in sourcetargetAccording to theobjcCall mode operation in the source code:

(2) malloc_zone_calloc

After enteringcallocProcess for specific memory opening up in usecallocIn the process of allocating memory, the first callmalloc_zone_callocmethods

According to thereturn ptrknownptrIt’s the point, butptr = zone->calloc(zone, num_items, size); Follow in will see a string of confusing code, and this source can not continue to follow:

(3) default_zone_calloc

So here’s the point!! To continue to follow the source code, you can use the following methods:

Method 1: analyze the zone

If a zone is of type malloc_zone_t, retval = malloc_zone_calloc(default_zone, num_items, size); The first parameter passed in zone is again default_zone, which you can trace back to as a static variable

static malloc_zone_t *default_zone = &virtual_default_zone.malloc_zone;
Copy the code
static virtual_default_zone_t virtual_default_zone
__attribute__((section("__DATA,__v_zone")))
__attribute__((aligned(PAGE_MAX_SIZE))) = {
	NULL.NULL,
	default_zone_size,
	default_zone_malloc,
	default_zone_calloc,
	default_zone_valloc,
	default_zone_free,
	default_zone_realloc,
	default_zone_destroy,
	DEFAULT_MALLOC_ZONE_STRING,
	default_zone_batch_malloc,
	default_zone_batch_free,
	&default_zone_introspect,
	10,
	default_zone_memalign,
	default_zone_free_definite_size,
	default_zone_pressure_relief,
	default_zone_malloc_claimed_address,
};
Copy the code

Zone ->alloc is default_zone_calloc

Method two: — console print

Sometimes printing is also a way to read the source code — printing tells you the actual calldefault_zone_calloc

Control + step into calloc source code

Add the breakpoint in the diagram, after the breakpoint is reached: press and holdcontrol + step intoWill also come:

There are two very important operations:

  • Create a truezone, i.e.,runtime_default_zonemethods
  • Use realzoneforcalloc

(4) nano_calloc

To continue printingzone->callocGet a hintnano_calloc:

Search for nano_calloc in the malloc source and find the method in the nano_malloc.c file, where the core code _nano_malloc_check_clear, allocates memory and returns a mature pointer PTR.

(5) _nano_malloc_check_clear

Analysis: /* FALLTHROUGH to helper zone */ FALLTHROUGH to helper zone */ That is, under normal circumstances go if judgment (if the space to be opened up is smaller than NANO_MAX_SIZE then nanozone_t malloc) NANO_MAX_SIZE=256

6 segregated_size_to_fit

Go to _nano_malloc_check_clear and don’t panic if you see a long code at this point. If you think again, we’re looking at the source code with a purpose — where the 48 in malloc_size comes from. Size = 40; slot_bytes = 48; error: 40->48;

  • Among themsegregated_next_blockPointer memory discovery algorithm, the purpose is to find the appropriate memory and return (recursively to find the appropriate memory space)
  • slot_bytesIs the salt of the encryption algorithm (its purpose is to make the encryption algorithm more secure, is essentially a string of custom numbers)

⑦ 16-byte alignment

Size = 40; after (40 + 16-1) >> 4 << 4, the result is 48, which is an integer multiple of 16.

Write in the back

Conclusion:

  • The attributes of the object are aligned to 8 bytes
  • The object itself is aligned to 16 bytes
    • Because memory is contiguous, 16-byte alignment avoids risk and fault tolerance, effectively preventing access overflow
    • At the same time, it also improves the efficiency of addressing access, which is commonly referred to as space swap time
  • Study harmoniously without being impatient. I’m still me, a different color of fireworks.