I. Definition of structure
The data members in a structure can be primitive types, arrays, Pointers, or other structures. Here is an example of a structure definition:
struct Student {
bool sex;
short int age;
char *address;
float grade;
char name[9];
};
Copy the code
Two, structure size
One oft-discussed problem is finding the Size of a structure, which is the number of bytes of memory used by an instance of a structure. The size of a structure depends on operating system word length, compiler, alignment, and many other factors. Therefore, there is no uniform result when confirming the size of a structure without the above constraints. In general, the size of the structure is calculated according to the following rules:
- The offset position of each data member in the structure is a multiple of the size of the data member itself.
- The size of the structure is a multiple of the size of the data member of the largest underlying type.
- If a structure is nested, the offset position of the nested structure member is a multiple of the size of the data member of the largest underlying type in the nested structure. The size of the nested structure is a multiple of the size of all the data members of the largest underlying type that are nested and in themselves.
Following the above rules, we can calculate the size of the above sample structure on a 64-bit system:You can see from the layout above:
- The sex data member is of type bool and it occupies
1
Bytes of memory, and is the first data member in the structure. The offset of the first data member always starts at 0 (0 is a multiple of the size of any data type). - The age data member is a short int, which occupies
2
Bytes of memory whose offset is 2(2 is a multiple of 2). We also see that there is a byte gap between the first data member and the second data member, which we call the padding. - The address data member is void *, which occupies
8
Bytes of memory, whose offset is 8(8 is a multiple of 8). This data member is set aside for alignment4
The padding space is one byte. - The grade data member is float, which occupies
4
Bytes of memory, whose offset is 16(16 is a multiple of 4). This member leaves no padding. - The name data member is char[9], which occupies
9
Bytes, whose offset is 20(20 is a multiple of 1). It also leaves no padding. - The size of the largest data member in the entire structure is void*, which occupies
8
Two bytes of memory, so the size of the structure is a multiple of eight32
Bytes. And you see it left behind in the tail3
One byte of padding.
As you can see from the above example, data members in a structure are not necessarily contiguous because of alignment, but may have padding gaps. This also leads to another problem: when we define the structure, if the data members are defined in an unreasonable order, it may lead to the occupation and waste of excess memory space. To achieve the best memory footprint, the order in which data members are defined in the above structure can be adjusted as follows:
struct Student {
bool sex;
char name[9];
short int age;
float grade;
char *address;
};
Copy the code
The optimized memory layout can be obtained:
How do you get the optimal order? My recommendation is to sort the underlying data types in order of size from smallest to largest (or from largest to smallest).
Finally, let’s take a look at the calculation rules of the size of the structure with nesting. The following structure is defined as an example:
struct A {
int a1;
char a2;
};
struct B {
char b1;
struct A b2;
};
Copy the code
The size of structure A occupies 8 bytes on A 64-bit system. What is the size of structure B and the offset of b2?
From the previous nesting rule definition, the largest underlying data type of any structure is int A1 in A, which takes up four bytes. So the size of B is 12, and the offset of B2 is a multiple of int length (4).
C. Data member and size of OC class
##### 1.oc class attributes both structs and classes are actually declarations and descriptions of collections of data, as is the case with OC class. Except in OC classes, you can define methods in addition to declaring data members. Of course, methods themselves do not occupy the storage space of objects.
Entity attributes declared in OC classes are eventually converted to data members. Each OC class also has an implicit data member isa, which isa pointer data member and is defined as the first data member of the class. So the following OC class definition:
@interface Student
@property short int age;
@property NSString *address;
@property float grade;
@property BOOL sex;
@end
Copy the code
If converted to a structure, it becomes:
struct Student {
void *isa;
BOOL _sex;
short int _age;
float _grade;
NSString *_address;
};
Copy the code
As you can see from the above definition, in addition to having one more ISA data member, the order of the data members has changed, and it is no longer in the order of the attributes defined in OC. The compiler automatically optimizes the order of attributes in the OC class, that is:
The order of attributes defined in the OC class is optimized at compile time by sorting data types from smallest to largest in size and alphabetically ordering data members of the same size.
Therefore, we do not need to consider the order in which attributes are defined when defining the OC class. The system will optimize these order to achieve the minimum memory footprint.
Finally, there is the memory footprint of the OC class instance objects. The object memory size of OC class is calculated according to the following rules:
- In a 64-bit system it is the sum of all the data members and is a multiple of 8, in a 32-bit system it is the sum of all the data members and is a multiple of 4.
- minimum
16
Bytes.
#####2. internal data members of the OC class Instance attributes defined in the OC class are converted to an underlined data member by default at compile time. The memory order of attribute data members is optimized. In practice, we can also define internal data members directly in the OC class, such as the following form:
@interface Student @property NSString *address; @property BOOL sex; @implementation Student {// implementation Student BOOL a[7]; NSString *b; } @endCopy the code
The above implementation defines two internal data members, A and B. When this happens, the compiler does not optimize the order of these internal data members. Instead, it arranges them in memory in the defined order, and prioritizes them over the attribute data members. So the final memory layout structure of the above example is:
struct Student {
void *isa;
BOOL a[7];
NSString *b;
BOOL _sex;
NSString *_address;
};
Copy the code
Therefore, I personally do not recommend defining internal data members in OC classes, as it will affect the final object memory footprint. If you do define, you need to consider the order in which these internal data members are defined in order to achieve the optimal memory footprint layout to reduce the footprint of the object’s memory instances. In the case of the above code, the best order of definitions on a 64-bit system would be as follows:
@interface Student @property NSString *address; @property BOOL sex; @implementation Student {// NSString *b; BOOL a[7]; } @endCopy the code