This is the 7th day of my participation in Gwen Challenge
The structure of the class
In iOS development, almost every object is an instance of a class. We have analyzed the structure of some classes before, including the following items:
Class
MetaClass
isa
SuperClass
property
instance method
ivar
class method
Interested friends can look at the article iOS bottom exploration 04 — iOS class structure analysis (on), this article will be on the basis of the last article from a higher dimension to explore about the class related knowledge;
clean Memory & dirty memory
Refer to video resources
Advancements in the Objective-C runtime – WWDC 2020 – Videos – Apple Developer
IOS Memory Details
Clean Memory/ Dirty Memory in iOS
Memory
For more information about Memory, see iOSMemroy
The description of virtual memory is inaccurate
We all know that iOS system and most operating systems use virtual memory technology. Virtual memory and physical memory and even hard disks are mapped one by one through mapping relationship. For ease of mapping and management, both virtual and physical memory are divided into units of the same size. The smallest unit of physical memory is called a Frame, and the smallest unit of virtual memory is called a Page. When the physical memory is insufficient, it is released through Page Out. When the physical memory needs to be used, it is loaded from the hard disk to the physical memory. When the Page Out fails to be retrieved from the CPU Page table, the Page fault is triggered and the released data is reloaded from the hard disk to the memory.
Clean Memory is not accurate
Although page faults affect performance due to reloading into memory, they relieve at least some of the stress on physical memory. This part of the Memory data that can be reloaded from the hard disk is called Clean Memory and mainly includes:
- The system framework
- The binary execution file of the application.
- Memory data is mapped to files.
Operating systems prefer Clean Memory to Dirty Memory;
Dirty memory is not accurate
If data is generated at runtime, it is difficult to recover once released. This part of data that cannot be reloaded from the hard disk is called Dirty Memory. The operating system wants as little of this data as possible;
The Class is loaded into memory for the first time
When a class is first loaded from disk into memory, its structure is shown below
Class is used by the Runtime
When a class is first used by the Runtime, it may be changed by the Runtime because the system does the following to it; Add new methods to the class by category; Using the Runtime API to manually add attributes and methods to the Class class_ro_t is read-only, so we need to store this information in class_rw_t
The Class is dynamically updated
When a class needs dynamic update, the dynamically updated part is extracted and stored in class_rw_ext_t. Its structure is shown in the following figure
The overall structure of a class
The structural evolution of class is shown belowThe class structure diagram is shown below
firstSubclass
All classes are linked by firstSubclass and nextSiblingClass Pointers, so the runtime iterates through all classes currently in use. Note that classes are lazily loaded, and only the corresponding firstSubclass class is used. Only in him will there be value;
The structure of the class
Properties & member variables & methods
- The best way to see the difference between member variables, attributes, and instance variables is to rewrite them in c++ using clang; The following code
Clang-rewrite-objc-fobjc-arc-fobjc-runtime = ios-14.2.0-isysroot / Applications/Xcode. App/Contents/Developer/Platforms/iPhoneSimulator platform/Developer/SDKs/iPhoneSimulator14.2. The SDK GCPerson.m -o GCPerson.cpp
The rewritten code is shown below
2. After keyword search, observe the difference between member variables and attributes, and find that the underlying are all member variables, but the attributes of the member variables are underlined _, attribute added set method and get method; 2 as shown in the figure 3.
- Member variables: Member variables of the base data type;
- Instance variable: a member variable of an object type (of the object type)
- Properties: underlined member variables + setter methods + getter methods
- methods
- while
method
The structure in OBJC is as follows
struct big {
SEL name;
const char *types;
MethodListIMP imp;
};
Copy the code
- While looking at the method, we found some strange characters, which are the type encoding of the member variable, through
command + shift + 0
Jump to documents, searchivar_getTypeEncoding()
, jump to the official website
See objective-C Type encodings as shown below
Code | Meaning |
---|---|
c | A char |
i | An int |
s | A short |
l | A longl is treated as a 32-bit quantity on 64-bit programs. |
q | A long long |
C | An unsigned char |
I | An unsigned int |
S | An unsigned short |
L | An unsigned long |
Q | An unsigned long long |
f | A float |
d | A double |
B | A C++ bool or a C99 _Bool |
v | A void |
* | A character string (char *) |
@ | An object (whether statically typed or typed id) |
# | A class object (Class) |
: | A method selector (SEL) |
[array type] | An array |
{name=type… } | A structure |
(name=type…) | A union |
bnum | A bit field of num bits |
^type | A pointer to type |
? | An unknown type (among other things, this code is used for function pointers) |
-
There is also numeric information in the method, such as @16@0:8
@
: id return value type;16
: Sum of memory occupied by all incoming parameters;@
: The type of the first argument;0
: The starting position of the first argument;:
: The type of the second argument;8
: The starting position of the second argument;
-
The encoding format is as follows:
[Return value type][total size of the input parameter][first parameter type][start position of the first parameter][second parameter type][start position of the second parameter][third parameter type][start position of the third parameter]…. [NTH parameter type][NTH parameter start position] outputs all parameters backward.
- You can also use the following methods
objc_copyIvar_copyProperies
To determine whether or notClass
The inside of theivar
和property
The difference between;
void objc_copyIvar_copyProperies(Class pClass){
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Ivar const ivar = ivars[i];
// Get the instance variable name
const char*cName = ivar_getName(ivar);
NSString *ivarName = [NSString stringWithUTF8String:cName];
NSLog(@"class_copyIvarList:%@",ivarName);
}
free(ivars);
unsigned int pCount = 0;
objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
for (unsigned int i=0; i < pCount; i++) {
objc_property_t const property = properties[i];
// Get the attribute name
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
// Get the attribute value
NSLog(@"class_copyProperiesList:%@",propertyName);
}
free(properties);
}
Copy the code
objc_setProperty
- When we look at the overwritten property set method, we see that some properties have
objc_setProperty
Some are passedMemory offset assignmentWhy are there these two differences? OC
The inside of theset
There are too many methods to write for every oneOC
theset
Methods add an underlying implementation,set
The essence of the method is to assign a value to a certain memory region. In order to facilitate the implementation of attribute assignment,objc
inOC
andThe underlying
Some intermediate methods have been added betweenobjc_setProperty
Methods; The bottom layer can beobjc_setProperty
Methods for different implementation;- In order for all of the top
ivar
theset
Method can be called directly toobjc_setProperty
LLVM implements the upper-layer set method at compile timesel->IMP
IMP redirected toobjc_setProperty
; - in
LLVM
Source searchobjc_setProperty
Keywords find the corresponding method implementation, find the GetPropertySetFunction() method, after reading the source code of LLVM, finally find that the property modified by copy will be added to the bottom layerobjc_setProperty
Methods; - why
objc_setProperty
Exists only incopy
Grooming? When copy decorates,objc_setProperty
Will performreallySetProperty()
Methods tozone
Memory copy to achieve the effect of memory copy. Don’t usecopy
Modified, the underlying is a simple memory offset after assignment, nocopy
The effect of;
Class method
- The FunctionStarts with MachOView. The FunctionStarts with MachOView.
2. The object method exists in the class. If a class method and an object method exist in the class, it is impossible to distinguish between the class method and the object method based on the method name. Do class methods exist in MetaClass? 3. Successfully obtain the class method from MetaClass after imitating the search process of object method; The LLDB mimics the steps of obtaining object methodsMetaClass
In themethod
, finally successfully found the class method;
(lldb) x/4gx metaClass // Step 1 Obtain the address of the metaClass
0x100004638: 0x00000001003540f0 0x00000001003540f0
0x100004648: 0x000000010034b360 0x0000e03100000000
(lldb) p/x 0x100004638 + 0x20 // Step 2 Structure pointer offset by 32 bytes
(long) $1 = 0x0000000100004658
(lldb) p (class_data_bits_t *)$1 / / step 3
(class_data_bits_t *) $2 = 0x0000000100004658
(lldb) p $2->data() // Step 4 Obtain data
(class_rw_t *) $3 = 0x0000000100719a60
(lldb) p *$3 // Important step 5
(class_rw_t) $4 = {
flags = 2684878849
witness = 0
ro_or_rw_ext = {
std::__1::atomic<unsigned long> = {
Value = 4294983992
}
}
firstSubclass = nil
nextSiblingClass = 0x00007fff884e2cd8
}
(lldb) p $4.methods() // Step 6 Obtain methods
(const method_array_t) $5 = {
list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
= {
list = {
ptr = 0x0000000100004180
}
arrayAndFlag = 4294984064
}
}
}
(lldb) p $5.list // Step 7 Obtain the list
(const method_list_t_authed_ptr<method_list_t>) $6 = {
ptr = 0x0000000100004180
}
(lldb) p $6.ptr // Step 8 Obtain the PTR
(method_list_t *const) $7 = 0x0000000100004180
(lldb) p *$7 // Step 9 Restore data
(method_list_t) $8 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1)
}
(lldb) p $8.get(0).big() // Step 10 Print the method information through big
(method_t::big) $9 = {
name = "sayHi"
types = 0x0000000100003ed2 "v16@0:8"
imp = 0x0000000100003a10 (KCObjcBuild`+[GCPerson sayHi] at main.m:30)
}
(lldb)
Copy the code
Method API summary
class_getClassMethod(pClass, @selector(sayHello));
// Get the instance method of the metaclass passed to the class; The instanceMethod of the MetaClass passed in to the class is found, so only the class method is found.class_getInstanceMethod(pClass, @selector(sayHello));
// Gets the instance method of the currently passed class. The passed metaclass gets the class method. The passed class gets the instance methodclass_copyMethodList(pClass, &count);
// Copy the method of the current class.class_getMethodImplementation(pClass, @selector(sayHello))
// Gets a method implementation of the current class, which is triggered if the current class cannot be found_objc_msgForward
;
isKindOfClass && isMemberOfClass
isKindOfClass:
- returns YES if the receiver is an instance of the specified class or an instance of any class that inherits from the specified class.
- Returns YES if the method caller is an instance object of the passed class, or if the caller is an instance object of a class in the successor chain of the passed class.
- The underlying call is
objc_opt_isKindOfClass(id _Nullable obj, Class _Nullable cls)
, but the effective version isOBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0)
; When usingobjc_opt_isKindOfClass(id obj, Class otherClass)
Class method or object method, the first parameterid obj
Is the class to call, the second argumentotherClass
Is a comparison class; fromobj
Class starts fetching from its parent classisa
withotherClass
Compare;
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if(slowpath(! obj))return NO;
Class cls = obj->getIsa();
if(fastpath(! cls->hasCustomCore())) {for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*) (id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
Copy the code
- Close inspection revealed
objc_opt_isKindOfClass(id obj, Class otherClass)
and-iskindofClass
As well as+iskindofClass
The realization of is basically the same;
+ isKindOfClass attention point
+isKindOfClass is supposed to determine if a class object is equal to a metaclass or a parent of a metaclass, but since the superClass of the NSObject metaclass refers to NSObject, So objcBOOL re1 = [(id)[NSObject Class] isKindOfClass:[NSObject Class]];
Normally the parent of a metaclass is a metaclass, you can never be equal to a class, but it happens that nsobject’s metaclass is also Nsobject’s class, so it’s equal;
isMemberOfClass:
returns YES if the receiver is an instance of the specified class. The method caller must be an instance object of the passed class to return YES.
void gcisKindofDemo(void) {Iskindofclass: Returns YES if the method caller is an instance object of the passed class, or if the caller is an instance object of a class in the successor chain of the passed class. Explain -
// iskindofClass: the method caller's isa or isa's superclass = the class passed in. Explain two
// Class objects are compared with metaclass or super metaclass,
//isMemberOfClass: The method caller must be an instance object of the incoming class to return YES.
BOOL re1 = [(id) [NSObject class] isKindOfClass:[NSObject class]].The parent of a metaclass is still a metaclass. It can never be equal to the class, but nsobject's metaclass is also nsobject's class, so it is equal;
BOOL re2 = [(id) [NSObject class] isMemberOfClass:[NSObject class]].// The left side of the metaclass is member, the right side is class, so I don't want to wait
BOOL re3 = [(id)[GCPerson class] isKindOfClass:[GCPerson class]].// The left side is an instance of the GCPerson metaclass or a parent of the GCPerson metaclass, and the right side is a class object of the GCPerson metaclass.
BOOL re4 = [(id)[GCPerson class] isMemberOfClass:[GCPerson class]].// The GCPerson metaclass is member; the GCPerson metaclass is member; the GCPerson metaclass is member;
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id) [NSObject alloc] isKindOfClass:[NSObject class]].// the left isa isa class, the right isa isa class, equal;
BOOL re6 = [(id) [NSObject alloc] isMemberOfClass:[NSObject class]].// the left isa isa class, and the right isa isa class, equal
BOOL re7 = [(id)[GCPerson alloc] isKindOfClass:[NSObject class]].// The isa on the left is the gcPerson class, and the isa on the right is the gcPerson class.
BOOL re8 = [(id)[GCPerson alloc] isMemberOfClass:[GCPerson class]].// The isa on the left is the gcPerson class, and the isa on the right is the gcPerson class.
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}
Copy the code