- What is the nature of the object?
- Basic definitions of Clang and XCRun.
- How to compile a.m file into a.cpp file.
- CPP file source code analysis.
- The difference between a structure and a union.
- How do I specify the bitfield of a member variable?
- NonpointerIsa pointer.
- What are the 64 bits in ISA?
- How property getters and setters are read and written.
Nature of object
Exploration methods:
Since Objective-C is a superset of C and C++, it can be explored by using the lightweight compiler Clang to rewrite Objective-C code and restore the underlying implementation of Objective-C code. Clang can compile.m files into.cpp files.
The Clang and xcrun
Clang:
Clang is an Apple-led, LLVM-based C, C++, objective-C lightweight compiler published under the BSD license, written in C++ and written in C++, with the goal (among other things) to surpass GCC.
xcrun
Xcrun is an Xcode script instruction set that is packaged on top of Clang for ease of use.
Preparation stage:
will.m
File compiled into.cpp
file
Clang compiler:
clang -rewrite-objc main.m -o main.cpp
Copy the code
Possible problems with overwriting files with UIKit libraries:
In file included from ViewController.m:9:
./ViewController.h:9:9: fatal error: 'UIKit/UIKit.h' file not found
#import <UIKit/UIKit.h>^ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~1 error generated.
Copy the code
Solutions:
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios13.0. 0 -isysroot /
Applications/Xcode.app/Contents/Developer/Platforms/
iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13. 0.sdk main.m
Copy the code
Xcrun compile:
/ / simulator
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
/ / real machine
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp
Copy the code
Analysis stage:
Declare an XJPerson class in the main.m file:
Open the terminal, enter the folder where main.m is located, run the above command to generate main. CPP, open main. CPP, search for XJPerson and start exploring, you will find many things worth paying attention to:
Concern 1:objc_object
The contents of the XJPerson object are found in the main. CPP file, XJPerson inherits from NSObject in the.m file, but is objc_Object in the.cpp file.
Conclusion 1: Comparing the.m file declaration with the.cpp file source code, we can conclude that the underlying nature of the object is objc_Object.
Concern 2:isa
Struct NSObject_IMPL NSObject_IVARS struct NSObject_IMPL Instead, search the.cpp file for the NSObject_IMPL structure (the XJPerson_IMPL structure has a nested NSObject_IMPL structure, which is a pseudo-inheritance of the structure).
Conclusion 2: NSObject_IVARS is the member variable ISA.
Concern 3:Class
Found in the.cpp file while searching for objc_Object
typedef struct objc_class *Class;
Copy the code
The underlying type of Class is a pointer to a structure such as objc_class *.
Concern 4:id
typedef struct objc_object *id;
Copy the code
Conclusion 4: The underlying type of id is objc_object *, so it can represent any type of object.
Concern 5: attributegetter
andsetter
methods
Getter method diagram:
Setters:
Conclusion 5: XJPerson * self, SEL _cmd, XJPerson * self, SEL _cmd, XJPerson * self, SEL _cmd, XJPerson * self, SEL _cmd, XJPerson * self, SEL _cmd, XJPerson * self, SEL _cmd That’s why you can use self in every method; Getters and setters store and value member variables by locating them with address offsets.
Structure, union, bit domain
Supplement your knowledge of point structures, unions, and bitfields before exploring ISA.
Case study:
#import <Foundation/Foundation.h>
// Do not specify a bitfield
// 4 bytes * 8 bits per byte = 32 bits 0000 0000 0000 0000 0000 0000
// The four BOOL values are 0 and 1, and the last four bits of one byte are used to represent 0000 1111
// Only 1 byte is needed, but 4 bytes are used and 3 bytes are wasted
struct XJCar1 {
BOOL front; / / 1 byte
BOOL back;
BOOL left;
BOOL right;
};
// When the bit field is specified, each BOOL takes up only 1 bit, and the entire structure takes up only 1 byte
struct XJCar2 {
BOOL front: 1; // Specify the bitfield to be 1bit, not 1 byte
BOOL back : 1;
BOOL left : 1;
BOOL right: 1;
};
// Structure: coexist, occupy the sum of the memory of all member variables, and then align with the structure internal memory alignment principle
struct XJTeacher1 {
char *name; // String, pointer, 8 bytes
int age; / / 4 bytes
double height; / / 8 bytes
}; // 8 + 4 + 8 = 20
// Union: mutually exclusive, all member variables share one memory space
union XJTeacher2 {
char *name; // String, pointer, 8 bytes
int age; / / 4 bytes
double height; / / 8 bytes
}; / / 8 bytes
int main(int argc, const char * argv[]) {
@autoreleasepool {
struct XJCar1 car1;
struct XJCar2 car2;
NSLog(@"---- car1 sizeof = %ld ---- car2 sizeof = %ld ----".sizeof(car1), sizeof(car2));
struct XJTeacher1 teacher1;
teacher1.name = "Cooci";
NSLog(@"---- struct XJTeacher1 (name = %s, age = %d, height = %f)-----sizeof = %ld----", teacher1.name, teacher1.age, teacher1.height, sizeof(teacher1));
teacher1.age = 18;
NSLog(@"---- struct XJTeacher1 (name = %s, age = %d, height = %f)-----sizeof = %ld----", teacher1.name, teacher1.age, teacher1.height, sizeof(teacher1));
union XJTeacher2 teacher2;
teacher2.name = "Cooci";
NSLog(@"---- union XJTeacher2 (name = %s, age = %d, height = %f)-----sizeof = %ld----", teacher2.name, teacher2.age, teacher2.height, sizeof(teacher2));
teacher2.age = 18;
NSLog(@"---- union XJTeacher2 (name = %s, age = %d, height = %f)-----sizeof = %ld----", teacher2.name, teacher2.age, teacher2.height, sizeof(teacher2));
}
return 0; } * * * * * * * * * * * * * * * * * * * * * * operation result * * * * * * * * * * * * * * * * * * * * * * * *2021- 06- 17 19:01:57.983162+0800 001- Consortium bitfield [3350:202587] ---- car1 sizeof = 4 ---- car2 sizeof = 1 ----
2021- 06- 17 19:01:57.983463+0800 001- Consortium bitfield [3350:202587] -struct XJTeacher1 (name = Cooci, age = 0, height = 0.000000) -- -- -- -- --sizeof = 24----
2021- 06- 17 19:01:57.983489+0800 001- Consortium bitfield [3350:202587] -struct XJTeacher1 (name = Cooci, age = 18, height = 0.000000) -- -- -- -- --sizeof = 24----
2021- 06- 17 19:01:57.983502+0800 001- Consortium bitfield [3350:202587] -union XJTeacher2 (name = Cooci, age = 15791, height = 0.000000) -- -- -- -- --sizeof = 8----
2021- 06- 17 19:01:57.983535+0800 001- Consortium bitfield [3350:202587] -union XJTeacher2 (name = , age = 18, height = 0.000000) -- -- -- -- --sizeof = 8----
Copy the code
Illustration:
Conclusion:
- Structure (
struct
All variables in) arecoexistence
Of, the advantage is “tolerant”, comprehensive; The disadvantage is that the allocation of memory space isextensive
, whether used or not, all allocated.- Structure (
struct
Memory can be optimized by specifying bitfields for member variables.- A consortium (
union
), the variables areThe mutex
The advantage is that the memory use is more delicate and flexible, saving memory space, the disadvantage isNot enough tolerance
.
nonPointerIsa
Objc_object ::initIsa () : object::initIsa () : object::initIsa () : object::initIsa ();
isa
It can be seen from the figure that the type of isa is isa_t. Control + command + left mouse button click isa_t to view its definition and find that isa_t isa union.
ISA_BITFIELD
Look at the macro definition ISA_BITFIELD and explore what’s inside the ISA pointer, divided into two architectural patterns.
# if __arm64__
// ARM64 simulators have a larger address space, so use the ARM64e
// scheme even when simulators build for ARM64-not-e.
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# define ISA_MASK 0x007ffffffffffff8ULL
# define ISA_MAGIC_MASK 0x0000000000000001ULL
# define ISA_MAGIC_VALUE 0x0000000000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 0
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t weakly_referenced : 1; \
uintptr_t shiftcls_and_sig : 52; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; // Whether the pointer is pure
uintptr_t has_assoc : 1; // Whether there is an associated object \
uintptr_t has_cxx_dtor : 1; // is there a C++ destructor \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; // weak reference \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; // hash table \
uintptr_t extra_rc : 19 // Reference count
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# endif
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; // Whether the pointer is pure
uintptr_t has_assoc : 1; // Whether there is an associated object \
uintptr_t has_cxx_dtor : 1; // is there a C++ destructor \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7ffffFE00000 */ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; // weak reference \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; // hash table \
uintptr_t extra_rc : 8 // Reference count
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# error unknown architecture for packed isa
# endif
Copy the code
__has_feature (ptrauth_calls) is introduced
- The __has_feature(ptrauth_calls) is used to determine whether the compiler supports pointer authentication.
- __has_feature: This function determines whether the compiler supports a feature.
- Ptrauth_calls: pointer authentication for arm64E architecture; Devices using Apple A12 or later A series processors (such as iPhone XS, iPhone XS Max, and iPhone XR or newer devices) support the ARM64E architecture.
- Reference links developer.apple.com/documentati… .
Illustration (ARM64) :
isa
Restoring class Information
Bit operation restore:
The above analysis of isa pointer stored in the data content, now through the bit operation to verify (M1 chip iMac, ARM64 architecture).
Case study:
#import <Foundation/Foundation.h>
#import "XJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
XJPerson *p1 = [XJPerson alloc];
NSLog(@ "% @", p1);
}
return 0; } * * * * * * * * * * * * * * * * * * * * * * the test operation, the result * * * * * * * * * * * * * * * * * * * * * * * * (LLDB)/x4gx p1
0x1009c2080: 0x000021a1000082c1 0x0000000000000000 Isa address 0x000021a1000082C1
0x1009c2090: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x000021a1000082c1 >> 3 // Move three places to the right to get the new address
(long) $1 = 0x0000043420001058
(lldb) p/x 0x0000043420001058 << 31 // Move the new address 31 bits to the left to get the new address
(long) $2 = 0x1000082c00000000
(lldb) p/x 0x1000082c00000000 >> 28 // Move the new address 28 bits to the right to get the new address
(long) $3 = 0x00000001000082c0
(lldb) po 0x00000001000082c0 // Print the new address to get XJPerson
XJPerson
Copy the code
Illustration:
The computer used for the test is M1 chip iMac, ARM64 architecture, and the ISA_BITFIELD of the generated object is the information from the 3rd to 35th bits of XJPerson. The restoration method is described as follows:
- through
x/4gx p1
Formatted outputp1
Object, starting withisa
Pointer to the0x000021a1000082c1
. - will
isa
The former3
One bit removed, i.e0x000021a1000082c1
Move to the right3
Who, throughp/x
Get a new pointer0x0000043420001058
. - will
isa
After the pointer28
One bit is removed because it just moved to the right3
One bit, so now I have to move to the left31
The number of bits, i.e0x0000043420001058
Moving to the left28 plus 3 is 31
Who, throughp/x
Get a new pointer0x1000082c00000000
. - The final will be
isa
The representativeXJPerson
The information of33
A bit reduction, i.e0x1000082c00000000
Move to the right28
One bit, passp/x
Get a new pointer0x00000001000082c0
. - The last
po
The output0x00000001000082c0
, the result obtained isXJPerson
.
ISA_MASK
Restore:
#import <Foundation/Foundation.h>
#import "XJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
XJPerson *p1 = [XJPerson alloc];
NSLog(@ "% @", p1);
}
return 0; } * * * * * * * * * * * * * * * * * * * * * * the test operation, the result * * * * * * * * * * * * * * * * * * * * * * * * (LLDB)/x4gx p1
0x1009c2080: 0x000021a1000082c1 0x0000000000000000 Isa address 0x000021a1000082C1
0x1009c2090: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x000021a1000082c1 & 0x0000000ffffffff8ULL // isa & ISA_MASK (0x0000000ffffffff8ULL), get the new address
(long) $1 = 0x00000001000082c0
(lldb) po 0x00000001000082c0 // Print the new address to get XJPerson
XJPerson
Copy the code
ISA_MASK:
- through
x/4gx p1
Formatted outputp1
Object, starting withisa
Pointer to the0x000021a1000082c1
. - will
isa
Pointer to the&
上ISA_MASK
, i.e.,0x000021a1000082c1
&0x0000000ffffffff8ULL
Through thep/x
Get a new pointer0x00000001000082c0
. po
The output0x00000001000082c0
, the result obtained isXJPerson
.
Note: iN iOS, data is read and written from right to left.
init
Source code analysis:
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
Copy the code
Conclusion: From the source code, init simply returns the object itself. This is the constructor of the factory design pattern, which provides an interface for subclasses to override and extend.
new
Source code analysis:
+ (id)new {
return [callAlloc(self.false/*checkNil*/) init];
}
Copy the code
The new method is alloc + init. Using the new method directly to initialize an object is not recommended because the initWithXXX constructor is not available.