preface
We are inOC underlying principles – object alloc understandingThe process of alloc method is introduced, and the very important method is shown in the figure below:articleOC basic principles – memory alignmentIn this article, we will examine the red box 3 and red box 4 methods: how to bind the ISA pointer to the requested memory address. Before exploring how to bind the ISA pointer to the requested memory address, we will first look at what the object is
The object nature
Create Student class
@interface Student : NSObject
@property (nonatomic, copy) NSString *name;
@end
@class Student;
@interface ViewController : UIViewController
@property (nonatomic, strong) Student *student;
@property (nonatomic, copy) NSString *schoolName;
@end
Copy the code
Because OC is a superset of C, we need to look at what OC code looks like in C, and here we need to use clang, right
Clang :Clang is a C/C++/Objective-C/ Objective-C++ compiler written in C++, based on LLVM and published under the LLVM BSD license. It is almost completely compatible with the GNU C specification (although there are some incompatibables, including some differences in compiler command options), and adds additional syntactical features such as C function overloading (which modifies functions via _ attribute_((overloadable)), One of its goals is to go beyond GCC.
Open the terminal and enter: xcrun -sdk iphoneOS clang -arch arm64-rewrite-objc ViewController. M -o ViewController-arm64.cpp
This is the schema we made for arm64, to compile the name: viewController.m, generate a new class name: viewController-arm64.cpp
Iphonesimulator13.5. SDK will follow this path to find UIKit. Clang-rewrite-objc-fobjc-arc-fobjc-runtime = ios-13.0.0-isysroot / Applications/Xcode. App/Contents/Developer/Platforms/iPhoneSimulator platform/Developer/SDKs/iPhoneSimulator13.5. The SDK main.m **
When we’re done, we go to the viewController.m directory and see that a new class is generated: viewController-arm64.cppWhen I opened the CPP file, there was a lot of stuff, over 70,000 lines of code. Search the ViewController directly to find what is shown belowSo the object is a structure and I’m going to focus on using clang, and the next few methods
- Clang-rewrite-objc main.m -o main. CPP compiles the object file into
- Xcrun – SDK iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
How is the ISA pointer bound to memory
How to bind the ISA pointer to the requested memory
The source code we use is objC4-781
An exploration of initInstanceIsa and initIsa methods
When we click on initInstanceIsa and the initIsa method we end up in the initIsa methodIsa appears in the red box of the method above. Let’s run the project and see it in action.After I run it, I found the red box method through the breakpoint, so what is the red box method
- Some say it’s a union,
- Isa_t (){} is the initialization method
- Class A CLS bound Class
- Uintptr_t bits typedef unsigned Long Long Integer 8 bytes
- Struct is a structure that contains an ISA_BITFIELD (the macro definition is used here because it is differentiated based on system architecture)
Let’s look at ISA_BITFIELD.
- Nonpointer: indicates whether pointer optimization is enabled for isa Pointers. 0 represents pure ISA pointer, 1 represents not only class object pointer, but also contains class information, object reference count, etc.
- Has_assoc: flag bit of the associated object. 0 does not exist and 1 exists
- Has_cxx_dtor: does the object have a destructor for C++ or Objc? If it has a destructor, the destructor logic needs to be done. If not, the object can be freed faster
- Shiftcls: Stores the value of the class pointer. With pointer optimization turned on, 33 bits are used to store class Pointers in the ARM64 architecture.
- Magic: Used by the debugger to determine whether the current object is a real object or has no space to initialize
- Weakly_referenced: If the object is pointed to or has been pointed to an ARC variable, objects without weak references can be released faster.
- Deallocating: An indication of whether the object is freeing memory
- Has_sidetable_rc: When the object reference technique is greater than 10, the variable is required to store carry
- Extra_rc: Represents the referential count of this object, which is actually the referential count minus 1. For example, if the object’s reference count is 10, extra_rc is 9. If the reference count is greater than 10, the following has_sideTABLE_rc is used.
The ISA pointer is designed this way to optimize performance and save space. Pointers are 8-byte, 64-bit, but a simple address pointer doesn’t take up that much space and would waste it if it was empty, so it saves space by adding other information about the object in the empty space
Above we can see that the isa_t type isa union and ISA_BITFIELD isa bit field
Union
When multiple data needs to share memory or multiple data needs to be fetched only for a moment at a time, you can use a union. In C Programming Language, unions are described as follows:
- A union is a structure;
- All its members have an offset of 0 relative to the base address;
- The structure should be large enough to accommodate the widest members;
- Its alignment should fit all its members;
A domain
Some information does not need to occupy a complete byte, but only a few or a binary bit. For example, when storing a switch quantity, there are only 0 and 1 states, and a binary can be used. In order to save storage space, and make processing simple.
Explore unions and bitfields
This may sound a little hollow, but to illustrate, let’s create a class: Car
@interface Car : NSObject
@property (nonatomic, assign) BOOL front;
@property (nonatomic, assign) BOOL back;
@property (nonatomic, assign) BOOL left;
@property (nonatomic, assign) BOOL right;
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
Car *car = [[Car alloc]init];
car.front = YES;
car.back = NO;
car.left = YES;
car.right = NO;
}
@end
Copy the code
Let’s print car and look at memory00, 00, 00, 01, 01, 01, 01 we’re using at least 4 bytes here to store these 4 properties and these properties right here are going to be a little bit wasted in space
How can we solve this problem by using the char type to represent these attributes
Char 0000 0001 char 0000 0001
We can use the first digit to mark 1 forward, the second digit to mark 1 backward, the third digit to mark 0 left, and the fourth digit to mark 1 right, so we only use 4 positions (the 4 positions are marked 8 in binary), which saves a lot of memory space
Now let’s transform Car
#import "Car.h" #define DirectionFrontMask (1 << 0) #define DirectionBackMask (1 << 1) #define DirectionLeftMask (1 << 2) #define DirectionRightMask (1 << 3) @interface Car (){// Union {char bits; Struct {// 0000 1111 char front: 1; char back : 1; char left : 1; char right : 1; }; } _direction; } @end @implementation Car - (instancetype)init { self = [super init]; if (self) { _direction.bits = 0b0000000000; } return self; } - (void)setFront:(BOOL)isFront { if (isFront) { _direction.bits |= DirectionFrontMask; } else { _direction.bits |= ~DirectionFrontMask; } NSLog(@"%s",__func__); } - (BOOL)isFront { return _direction.front; } - (void)setBack:(BOOL)isBack { _direction.back = isBack; NSLog(@"%s",__func__); } - (BOOL)isBack { return _direction.back; } - (void)setRight:(BOOL)right { _direction.right = right; } - (BOOL)isRight { return _direction.right; } - (void)setLeft:(BOOL)left { _direction.left = left; } - (BOOL)isLeft { return _direction.left; } @endCopy the code
Run the interrupt point again and print the image below Now we’re only using one byte, three less than before, so we’re using 0f to represent four attributes. The printouts are all correct. But there’s one front that’s nil, because we just wrote the return method prefixed with is
Going back to the above approach, let’s explore the internal logic of ISA
How to assign an isa pointer inside
We place the breakpoint in initIsa, execute the initInstanceIsa method, and next we tell the compiler to go to bits and print CLS and bits nil, indicating no assignmentClick Next, assign the bits, print the bits, and CLS finds that the value is already there. This indicates that the assignment of bits is also synchronized with the assignment of CLS to bind memory to address
We print the whole newisa(nonpointer = 1) Magic = 59 (nonpointer = 1) We can enter the ISA_MAGIC_VALUE value into the calculator as shown belowLet’s put 59 into the calculator again, and look at binaryThat’s why magic is 59. This tells us which attributes are initialized. Let’s verify that the shiftcls object isa stores the value of a class pointer. According to the x86 architecture, remove the first property of the object with the lower 3 bits and the higher 17 bitsLet me draw a picture
As you can see from the figure above, the pointer is exactly the same as the value of the first property of the class object, which verifies that isa’s Shiftcls stores the value of the class pointer.
In fact, in the source code, there is already such an operationObject_getClass (id obj); object_getClass(id obj);
Supplement: After nearly an hour of sorting, according to the isa pointer above, sorting the table, deepen the impression:
Added: ISA pointer position map, ARM64 and X86
The last
When it comes to the isa internal structure basically finished, tell the clang, isa internal structure, found that the print memory address is very interesting, be free and at leisure is continue to knocked at isa pointer content are many, their understanding of the object before the isa pointer to class, the class of the isa point to point to the metaclass, yuan metaclass class to root, root metaclasses to itself. Let’s try this idea, and it works, and we go straight to the picture aboveDiagrams are also used to explain If I may be honest, ISA points to a class object -> class -> metaclass -> root metaclass -> root metaclass
After more understanding, may play some other.