1. The Runtime Runtime

1.1 concept

Instead of run-time, there is a compile-time concept

  • Compile-time: As we develop, the programming software checks for errors in methods, syntax, references, and environment. Will alert you to errors or warnings when writing code.
  • Runtime: When code is running, it becomes runtime when it is loaded into memory. An error occurs at runtime, causing the program to crash.

1.2 Three forms of call methods at runtime

  1. Oc layer, code layer, our daily development calls our custom methods[kbPerson test];
  2. System API calls, for example[:isEqualto:]; [isKindOf:]; Etc.
  3. The underlying implementation, for examplemsgSend.class_getInstanceSize, etc.

Above:codeWrite code for everyday use,CompilerFor the compiler layer, code will be translated into some intermediate state language, and some LLVM compiler optimizations will be made, such as alloc method optimization to execute objc_alloc method.runtime system libararyIt’s the underlying library.

2. The nature of the method

We’re in main

 KBPerson *kbPerson = [KBPerson alloc];
 [kbPerson sayNB];
Copy the code

Using clang to compile the file main.cpp,

    KBPerson *kbPerson = ((KBPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("KBPerson"), sel_registerName("alloc"));
    ((void (*)(id, SEL))(void *)objc_msgSend)((id)kbPerson, sel_registerName("sayNB"));
Copy the code

The essence of the method is: ((void (*)(id, SEL))(void *)objc_msgSend)() objc_msgSend SEL is the name of the method, so we can write it like the system does: #import

, then the objc_msgSend setting is not checked

When adding parameters:

[kbPerson sayNB:@"NB"];
objc_msgSend(kbPerson, @selector(sayNB:),@"NB");

/*((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)kbPerson, sel_registerName("sayNB:"), (NSString *)&__NSConstantStringImpl__var_folders_89_gg12jph17dg35tdtmwzd58180000gn_T_main_bb502a_mi_1); * /
Copy the code

Add corresponding parameter types and parameters. Instead of implementing the sayHello Method in KBTeacher, we implement it in its parent class, albeit with the caveat that Method definition for ‘sayHello’ not found does not crash and prints the Method name

@interface KBPerson : NSObject
-(void)sayNB:(NSString*)name;
@end
@implementation KBPerson

-(void)sayNB:(NSString*)name{
    
    NSLog(@"%s",__func__); } - (void)sayHello{
    NSLog(@"%s",__func__);
}
@end
@interface KBTeacher:KBPerson
-(void)sayHello;
@end
@implementation KBTeacher

@end
@end
int main(int argc, const char * argv[]) {
    
    
    KBPerson *kbPerson = [KBPerson alloc];
    [kbPerson sayNB:@"NB"];
    KBTeacher *kbTeacher = [KBTeacher alloc];
    [kbTeacher sayHello];
    
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}
Copy the code

Although it will be stated that the subclass will query the parent class method, if there is one will call it, we look at the source

objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
    OBJC_AVAILABLE(10.0.2.0.9.0.1.0.2.0);
    
struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained _Nonnull id receiver;

    /// Specifies the particular superclass of the instance to message. 
#if! defined(__cplusplus) && ! __OBJC2__/* For compatibility with old objc-runtime.h header */
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
    /* super_class is the first class to search */
};
Copy the code

We can assign objc_super and call objc_msgSendSuper

    KBTeacher *kbTeacher = [KBTeacher alloc];
    struct objc_super  kb_super;
    kb_super.receiver  = kbTeacher;
    kb_super.super_class = KBPerson.class;

    objc_msgSendSuper(&kb_super, @selector(sayHello));
Copy the code

Super_class is the first to call the lookup method, and if it can’t find it, it goes to its parent class to find the method. So the essence of a method is sending a message, objc_msgSend

3. _objc_msgSend General process

We know that from the assembly

Enter the objc_msgSend.

It also verifies that the method execution is the call to objc_msgSend. We enter the source code

#endif

	ENTRY _objc_msgSend // The method is passed in with the recipient and the method name
	UNWIND _objc_msgSend, NoFrame

	cmp	p0, #0// Assign the receiver to register P0 and determine whether the receiver is 0
        // If it is 0, do the following
        #if SUPPORT_TAGGED_POINTERS// Whether target_pointers are supported
	b.le	LNilOrTagged// Go to the LNilOrTagged: method
         #else
	b.eq	LReturnZero // go to the LReturnZero: method below, and send an empty method
#endif
	ldr	p13, [x0] // Assign the recipient's first address isa to p13, p13 = isa
	GetClassFromIsa_p16 p13, 1, x0	// Get the class from this method and assign it to p16 = class, passing in p13, the receiver's ISA, 1, and the receiver.
LGetIsaDone:
	// calls imp or objc_msgSend_uncached
	CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached// Cache lookup for isa

#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
	b.eq	LReturnZero		// nil check checks if the object is 0 and sends an empty method if 0
	GetTaggedClass  // Get the class of the little endian object
	b	LGetIsaDone/ / get the isa
// SUPPORT_TAGGED_POINTERS
#endif

LReturnZero:
	// x0 is already zero
	mov	x1, #0
	movi	d0, #0
	movi	d1, #0
	movi	d2, #0
	movi	d3, #0
	ret

END_ENTRY _objc_msgSend/ / end
Copy the code

Tagged Pointer: stores small objects, such as NSNumber and NSDate. The value of the Tagged Pointer is no longer an address, but a real value. So, it’s not really an object anymore, it’s just a normal variable in an object’s skin. So, its memory is not stored in the heap and does not require malloc and free! Three times more efficient at reading memory and 106 times faster at creation time!

  1. First, determine whether the receiver is empty. If it is empty, determine whether the small end mode is supported. If it is not, directly return a null; if it is supported, enter LNilOrTagged: method to determine whether the object is nil.

  2. Store the recipient’s initial address in p13, i.e. P13 = ISA; Isa, 1, and the receiver are passed through the GetClassFromIsa_p16 method. P16 = class;

  3. LGetIsaDone:, pass NORMAL, _objc_msgSend and __objc_msgSend_uncached to get cached ookup.

The detailed flow of objc_msgSend will be explored in the following chapters