Struct objc_classobjc_class objc_object
Before studying ISA, let’s examine the relationship between objC_class and objc_Object.
objc_object
Let’s start with the objc_Object structure
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
Copy the code
typedef struct objc_class *Class;
Copy the code
We see that the objc_object structure has only one isa of Class type. We see that the definition of Class isa structure pointer to objc_class.
objc_class
We’re looking at the structure of objc_class
struct objc_class { Class _Nonnull isa OBJC_ISA_AVAILABILITY; #if ! __OBJC2__ Class _Nullable super_class OBJC2_UNAVAILABLE; const char * _Nonnull name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE;Copy the code
Note here that this structure has been marked OBJC2_UNAVAILABLE is currently a deprecated structure.
In fact, the objC_class structure being used in the current OBJC project is the one below
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits;
}
Copy the code
Structs can be inherited in C++, and we can see that objc_class inherits from objc_object.
Since there is only one ISA pointer in objc_Object, the structure of objc_class is simplified as follows:
struct objc_class : objc_object {
// Class ISA;
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits;
}
Copy the code
Class _Nonnull ISA: Isa pointer to the current Class object
Class superclass: superclass
Cache_t cache: Cache information for a class (covered later)
Class_data_bits_t bits: Important information about a class is stored in class_datA_RW_t and class_datA_ro_t
Class_data_rw_t: stores the attributes, instance methods, and protocol information of the main class
Class_data_ro_t: Stores information about member variables
Summary: Objects will be defined as objc_Object structures, classes will be defined as objc_class objects, and objC_class inherits from ObjC_objec so classes are also special objects.
What exactly is isa
OC = struct objc_object = struct objc_object = struct objc_object = C++
Clang
- Clang is a lightweight compiler for C, C++, and Objective-C. Source code is published under the BSD protocol. Clang will support its normal lambda expressions, simplified handling of return types, and better handling of constEXPr keywords.
- Clang is an Apple-led, LLVM-based C/C++/Objective-C compiler
- Clang already fully supports the C++11 standard and has begun implementing C++1y features (i.e. C++14, the next minor update to C++). Clang will support its normal lambda expressions, simplified handling of return types, and better handling of constEXPr keywords. 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 language specification (although there are some incompatibables, including some differences in compiler command options), and adds additional syntactic features such as C function overloading (attributing ((overloadable)), One of its goals is to go beyond GCC.
- OC code can be compiled into C++ code using XCode’s own clang
- Xcrun-sdk iphoneOS clang-arch arm64-rewrite-objc-fobjc-arc-fobjc-Runtime =ios-8.0.0 main.m(source file path) -o Main-arm64.cpp (target file path)
- Xcrun: Xcode Run
- – SDK iphoneOS: iOS
- Clang: XCode comes with a compilation environment
- Arch ARM64: Compiles artifacts for the corresponding architecture
- -rewrite-objc: treats the file as the target C++ file
- -fobjc-arc: indicates the memory management mode
- -fobjc-Runtime =ios-8.0.0: Runtime Version This parameter is required when runtime is required
Clang conversion
@interface YJCar : NSObject
@property (copy, nonatomic) NSString *name;
- (void) sayNB;
@end
@implementation YJCar
- (void) sayNB
{
}
@end
Copy the code
The code to convert to C++ looks like this:
#ifndef _REWRITER_typedef_YJCar
#define _REWRITER_typedef_YJCar
typedef struct objc_object YJCar;
typedef struct {} _objc_exc_YJCar;
#endif
extern "C" unsigned long OBJC_IVAR_$_YJCar$_name;
struct YJCar_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *__strong _name;
};
// @property (copy, nonatomic) NSString *name;
// - (void) sayNB;
/* @end */
// @implementation YJCar
static void _I_YJCar_sayNB(YJCar * self, SEL _cmd) {
}
static NSString * _I_YJCar_name(YJCar * self, SEL _cmd) { return (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_YJCar$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_YJCar_setName_(YJCar * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct YJCar, _name), (id)name, 0, 1); }
Copy the code
We can see that YJCar is converted to a typedef struct objc_object YJCar, objc_object structure.
Struct NSObject_IMPL NSObject_IVARS is actually isa pointer
struct NSObject_IMPL {
__unsafe_unretained Class isa;
};
Copy the code
Union and bitfield
Before exploring ISA, we need to first understand the union and the union bit domain knowledge
Union and structure
Union and structure are both basic construction data types
Structure (struct)
Structure: The integration of different data into a whole, its variables are coexisting, whether or not the use of variables will open up memory space.
- Advantages: Large storage capacity, strong inclusiveness, and no interaction between members
- Disadvantages: Since all members of a structure are allocated memory (unused), unused memory is wasted
Union
Union: Also called Commons, to combine different data into a whole, its variables are mutually exclusive, all members of the same memory. Consortium adopts memory overlay technology, and only one member value can be saved at a time, namely:
- Advantages: All members share a memory segment to save memory space
- Disadvantages: Weak inclusion, mutually exclusive members, can save only one member value at a time
The difference between the two
- Memory footprint
-
- Structure: Each member occupies different memory and has no effect on each other
- Union: Each memory occupies the same memory. Modifying a member affects all other members
- Memory allocation
-
- Struct: Opens memory greater than or equal to the sum of all members (alignment may result in free space)
- Union: the memory occupied by the largest member
The ISA to the
- Before the ARM64 architecture, ISA was a plain pointer that stored the memory addresses of Class and meta-class objects
- Starting with the ARM64 architecture, ISA was optimized to become a common body (Union) structure, and bitfields were used to store more information
- The isa contents and ISA_MASK bits need to be calculated to obtain the specific Class and meta-class memory address
-
- A Commons is similar to a structure in that the elements in a Commons share the same memory space
- The memory size of the shared body is at least the size of the memory space of the largest element in the shared body
-
- The Commons can allocate specific memory sizes for different elements by means of bit-fields
- The shared internal memory is discharged from the back to the front
-
- Isa common body structure
-
-
- Nonpointer:
-
-
-
-
- 0: indicates a common pointer that stores the memory address of Class and meta-class objects
- 1: indicates that bitfields are optimized to store more information
-
-
-
-
- Has_assoc:
-
-
-
-
- Is the associated object set? If not, it will be released faster
-
-
-
-
- Has_cxx_dtor:
-
Does C++ destructor (.cxx_destruct) exist? If not, the release will be faster
- shiftcls:
-
- Store the memory address information of Class and meta-class objects
- Magic:
-
- Used during debugging to tell if an object has not been initialized
- Weakly_referenced:
-
- If a variable is pointed to or has been pointed to ARC, if not, it will be released faster
- Deallocating:
-
- Object is freeing memory
- Extra_rc:
-
- The value stored inside refers to the counter decrement by 1
- Has_sidetable_rc:
-
- Whether the reference counter is too large to be stored in isa
- If it is 1, the reference counter is stored in an attribute of a class called SideTable
Shiftcls is somewhat different on ARM and X86 platforms
# 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; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# 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; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# error unknown architecture for packed isa
# endif
Copy the code
We can see that the ARM64 platform takes up between 3 and 36 bytes for a total of 33 bytes
uintptr_t shiftcls : 33; /MACH_VM_MAX_ADDRESS 0x1000000000/
On X86, which is our simulation, it takes between 3 and 47 bytes, which is 44 bytes
uintptr_t shiftcls : 44; /MACH_VM_MAX_ADDRESS 0x7fffffe00000/
Get the Mate metaclass via Shitcls
Take the X86 platform as an example
Mode one: bit operation movement
To get the Shitcls information, we simply move the value of ISA_t 3 bytes to the right, 20 bytes to the left, and 17 bytes to the right
We can do that by doing bit operations to shift left and right
Example:
Let’s go ahead and see what is isa for metaclasses
Method two: and operation
From the above LLDB analysis, we already know the isa structure, and we also know the ISA pointing relationship of the instance object.
Conclusion:
Instance object ISA -> class object ISA -> metaclass ISA -> root primitive isa- > root primitive isa- > root primitive isa- > root primitive isa- > root primitive isa- > root primitive isa
Establishment of isa and instance object relationships
In the previous section we looked at the alloc process by associating our created objC object with the class using the initIsa function in createInstanceFromZone.
Let’s first look at the structure code for initIsa
objc_object::initIsa(Class cls)
{
initIsa(cls, false, false);
}
Copy the code
We find that the initIsa() method is called inside the structure
SuperClass
Superclass stores the superclass information of the current class. Instance objects do not have a Supperclass. Only class objects have a superclass. This is why the objc_Object structure only has ISA.
Conclusion:
SupperClass–> Parent Class–> parent Class–>… –>NSobject–>nil
Method call flow
Method calls are divided into instance method calls and class method calls. Before studying the call, we first understand the storage location of the heart method.
Instance object (instance object)
- The ISA of an instance object points to class
- When an object method is called, the class object is first found through ISA and then called when the object method is found
- The ISA of class points to the metaclass’s class, which is mate_class
- When calling a class method, you need to find the class through ISA, find the metaclass through isa of class, and then call it when you find the class method
Class object (Class object)
- When a Student instance calls a Person instance method, it finds the Student class through instance isa, then finds Person in the superclass of Student’s class, and finally finds the method to call.
Mate_class object (metaclass object)
- When a Student class calls a Person object method, the Student’s isa is used to find the Student’s mate_class, and the superclass of the Student’s mate_class is used to find the Person’s mate_class. Finally find the class method of Person to call
- This is special: when looking for a method of the superclass and finding NSObject without finding a method, the system will look again, which is to find an instance method of the class object of the root metaclass. If it finds one, it will call it, and if it doesn’t find one, it will report an error.
Finally, let’s look at isa and Superclass and see how it all works
Universal figure
KC teacher split the diagram
\