• 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.mFile compiled into.cppfile

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: attributegetterandsettermethods

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:

  1. Structure (structAll variables in) arecoexistenceOf, the advantage is “tolerant”, comprehensive; The disadvantage is that the allocation of memory space isextensive, whether used or not, all allocated.
  2. Structure (structMemory can be optimized by specifying bitfields for member variables.
  3. A consortium (union), the variables areThe mutexThe 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) :

isaRestoring 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:

  1. throughx/4gx p1Formatted outputp1Object, starting withisaPointer to the0x000021a1000082c1.
  2. willisaThe former3One bit removed, i.e0x000021a1000082c1Move to the right3Who, throughp/xGet a new pointer0x0000043420001058.
  3. willisaAfter the pointer28One bit is removed because it just moved to the right3One bit, so now I have to move to the left31The number of bits, i.e0x0000043420001058Moving to the left28 plus 3 is 31Who, throughp/xGet a new pointer0x1000082c00000000.
  4. The final will beisaThe representativeXJPersonThe information of33A bit reduction, i.e0x1000082c00000000Move to the right28One bit, passp/xGet a new pointer0x00000001000082c0.
  5. The lastpoThe output0x00000001000082c0, the result obtained isXJPerson.

ISA_MASKRestore:


#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:

  1. throughx/4gx p1Formatted outputp1Object, starting withisaPointer to the0x000021a1000082c1.
  2. willisaPointer to the&ISA_MASK, i.e.,0x000021a1000082c1 & 0x0000000ffffffff8ULLThrough thep/xGet a new pointer0x00000001000082c0.
  3. poThe 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.