This is the 9th day of my participation in Gwen Challenge
Cache expansion supplement
Translation memory
We used the following method to find sel and IMP data in the previous article
When we couldn’t find the data in $12, we shifted the pointer and finally found the data:
(lldb) p $9.buckets()[4]
(bucket_t) $19 = {
_sel = {
std::__1::atomic<objc_selector *> = "" {
Value = ""
}
}
_imp = {
std::__1::atomic<unsigned long> = {
Value = 45536
}
}
}
Copy the code
$buckets()[4] Bucket_t is an array, and bucket_t is a bucket structure. Buckets ()[4] Bucket_t is an array.
$9.buckets()[4] is called fetching memory translation. $9.buckets()[4] is a pointer to a structure. $9.Buckets ()[4] is equal to $12+4.
_bucketsAndMaybeMask
parsing
In the previous operation we had this data structure:
So what is this _bucketsAndMaybeMask thing? Let’s look at it through LLDB:
Check the buckets() method in action:
Retrieve addr from _bucketsAndMaybeMask and cast to hexadecimal address after operation with bucketsMask.
_bucketsAndMaybeMask stores the starting address of buckets()
Cache reading process analysis
In the introduction to cache_t we learned that the method is used to insert the cache. When is that done? We break our insert point and execute code:
Insert (log_and_fill_cache); insert (log_and_fill_cache);
The stack shows that lookUpImpOrForward calls log_AND_fill_cache:
This is the underlying message flow of C++
Runtime understanding of runtime
The runtime overview
To understand the message flow, you must first understand the Runtime
At compile time the compiler will help us translate the source code into code that the machine can recognize, or some intermediate state of the code. In this process, the compiler will help us with grammar analysis, type checking and other work; Run-time is when our program is already running and loaded into memory, where all operations are taking place.
Runtime comes in two versions:
Legacy
The version is an earlier versionObjective - 1.0 C
for32
bitMacOS X
In the systemModern
Indicates the current versionObjective - 2.0 C
foriPhone
andMacOS X v10.5
After the64
A system of
Objective-C Runtime Programming Guide
Three ways to call the Runtime
- OC method; Such as
[peron run]
- NSObject interface; Such as
isKindoOfClass
- Objc underlying Api; Such as
class_getInstanceSize
Let’s look at the code below
@interface Teacher : NSObject
- (void)say:(NSString *)string;
- (void)run;
@end
@implementation Teacher
- (void)say:(NSString *)string {
NSLog(@"%s-->%@", __func__,string);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Teacher *teacher = [Teacher alloc];
[teacher say:@"hello"];
[teacher run];
}
return 0;
}
Copy the code
Teacher has two methods say: and run. Say: is implemented, but run is not implemented. This code will compile successfully at compile time, but will crash at run time with an error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Teacher run]: unrecognized selector sent to instance 0x1007aa0a0'
terminating with uncaught exception of type NSException
Copy the code
Take a look at the code in the CPP file:
Teacher *teacher = ((Teacher *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Teacher"), sel_registerName("alloc"));
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)teacher, sel_registerName("say:"), (NSString *)&__NSConstantStringImpl__var_folders_7z_qx6zs9gj2s58dv3ps88mf9pr0000gn_T_main_388285_mi_1);
((void (*)(id, SEL))(void *)objc_msgSend)((id)teacher, sel_registerName("run"));
Copy the code
For easy viewing, it is simplified as follows:
Teacher *teacher = ((Teacher *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Teacher"), sel_registerName("alloc"));
objc_msgSend)(teacher, sel_registerName("say:"), __NSConstantStringImpl__var_folders_7z_qx6zs9gj2s58dv3ps88mf9pr0000gn_T_main_388285_mi_1);
objc_msgSend(teacher, sel_registerName("run"));
Copy the code
Our common OC code is interpreted using runtime at the bottom
The invocation of the OC method is essentially the process of sending messages
objc_msgSend(id self, SEL _cmd)
Copy the code
id self
For the receiver of the messageSEL _cmd
Is the body of the messagesel
+parameter
Objc_msgSend and objc_msgSendSuper
Now that we understand the nature of this, we can implement the method call directly through objc_msgSend:
You need to change the Enable Strict Checking of objc_msgSend Calls in Build Settings to NO. The default value is YES
We add a method to Teacher, talk, and implement:
- (void)talk;
- (void)talk {
NSLog(@"-->%s", __func__);
}
Copy the code
Method call successful
We create a Person class, let Teacher inherit from the Person class, implement the run method in the Person class, the code is as follows:
@interface Person : NSObject
@end
@implementation Person
- (void)run {
NSLog(@"-->%s", __func__);
}
@end
@interface Teacher : Person
- (void)say:(NSString *)string;
- (void)run;
- (void)talk;
@end
@implementation Teacher
- (void)say:(NSString *)string {
NSLog(@"%s-->%@", __func__,string);
}
- (void)talk {
NSLog(@"-->%s", __func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Teacher *teacher = [Teacher alloc];
[teacher say:@"hello"];
[teacher run];
}
return 0;
}
Copy the code
Operating Projects:
There is no crash, although the subclass Teacher does not have an implementation of the run method, but directly found the parent class Person run method
The CPP file is generated:
Teacher *teacher = ((Teacher *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Teacher"), sel_registerName("alloc"));
((void(*) (id, SEL, NSString(*))void *)objc_msgSend)((id)teacher, sel_registerName("say:"), (NSString *)&__NSConstantStringImpl__var_folders_7z_qx6zs9gj2s58dv3ps88mf9pr0000gn_T_main_54da99_mi_3);
((void(*) (id, SEL))(void *)objc_msgSend)((id)teacher, sel_registerName("run"));
Copy the code
Objc_msgSend sends a message to the parent class. The CPP file contains a method objc_msgSendSuper that sends a message to the parent class. The objc_msgSendSuper method definition can be found in objC source code:
OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
Copy the code
It has two parameters:
- The structure of the body
objc_super
SEL
Objc struct objc_super
Since the current environment satisfies __OBJC2__–Objective-C 2.0, the structure can be simplified as:
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
__unsafe_unretained _Nonnull Class super_class;
/* super_class is the first class to search */
};
Copy the code
In our project, we try to call the run method using objc_msgSendSuper:
Explanation: Super_class is the first class to look up. If no method is found, look up the superclass and then look up. Because NSObject doesn’t have a run method, and NSObject’s parent class is nil, you can’t pass NSObject here;
_objc_msgSend Process analysis
Let’s analyze _objc_msgSend process analysis implementation, which is implemented in assembly, we in objC source code to analyze:
Not much code, explained as follows:
_objc_msgSend
Unwinding _objc_msgSend, NoFrame CMP P0, #0 // register p0 is the first parameter 'id' of objc_msgSend, Compare with 0 to determine whether a message receiver is available. #if SUPPORT_TAGGED_POINTERS // Determine whether the condition supports tagged pointer #endif LDR p13, [x0] #endif LDR p13, P13 = receiver's isa GetClassFromIsa_p16 p13, 1, x0 // GetClassFromIsa_p16 is explained below, final result p16 = receiver's class LGetIsaDone: // Instruction completion is actually a process of finding the class through the receiver, Cached // calls IMP or objc_msgSend_uncached CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached #if SUPPORT_TAGGED_POINTERS LNilOrTagged: b.eq LReturnZero // nil check GetTaggedClass b LGetIsaDone // SUPPORT_TAGGED_POINTERS #endif LReturnZero: // return nil // x0 is already zero mov x1, #0 movi d0, #0 movi d1, #0 movi d2, #0 movi d3, Ret END_ENTRY _objc_msgSend // _objc_msgSend EndCopy the code
GetClassFromIsa_p16
// GetClassFromIsa_p16 p13, 1, x0; src=p13=isa needs_auth=1 auth_address=x0 .macro GetClassFromIsa_p16 src, needs_auth, auth_address /* note: auth_address is not required if ! Needs_auth */ #if SUPPORT_INDEXED_ISA // needs_auth */ #if SUPPORT_INDEXED_ISA / \src // optimistically set dst = src tbz p16, #ISA_INDEX_IS_NPI_BIT, 1f // done if not non-pointer isa // isa in p16 is indexed adrp x10, _objc_indexed_classes@PAGE add x10, x10, _objc_indexed_classes@PAGEOFF ubfx p16, p16, #ISA_INDEX_SHIFT, #ISA_INDEX_BITS // extract index ldr p16, [x10, p16, UXTP #PTRSHIFT] // load class from array 1: Needs_auth == 0 // Needs_auth =1 does not meet _cache_getImp takes an Authed Class already MOV P16, \ src. else // 64-bit Packed ISA ExtractISA P16, \ SRC, \auth_address P16 is the receiver's class.endif #else // 32-bit raw ISA MOV P16, SRC # endif.endmacroCopy the code
ExtractISA
// ExtractISA p16, SRC, auth_address. macro ExtractISA and $0, $1, #ISA_MASK // $0=p16, $1= SRC =isa, $1 and ISA_MASK to get the class stored in $0= p16.endmacroCopy the code
The next chapter continues at……