Runtime’s essence (3) —- objcmsgsend The method call in

2022-12-30   ES  

OC is actually the call of the objc_msgsend function
OBJC_MSGSEND’s execution process can be divided into three major stages:

  1. Message Send
  2. dynamic method analysis
  3. Message forwarding

When the call method is called, the Objc_msgsend function is executedobjc_msgSend(<#id _Nullable self#>, <#SEL _Nonnull op, ...#>)
The first parameter is a message receiver, and the second parameter is the method name.
We found the specific implementation of Objc_msgsend in the source code, which was implemented by assembly. By analyzing the source code, it can be seen that when the message receiver is empty, it is the following operations:

void objc_msgSend(id receiver, SEL selector)
{
    
    if(receiver == nil) return;
    // 1. Determine whether the receiver is nil
	
	// 2. Find the cache
    
    // 3. Execute the following code
}

, that is, when the air object call method, return directly.


The process of the message sending phase is roughly:
在这里插入图片描述

When an object calls an unable to find a method, it will enter the dynamic method analysis stage.

dynamic method analysis phase, first judge whether there have been dynamic methods before:
If dynamic method analysis has been performed beforetriedResolver = YES, no dynamic method analysis;
If there is no dynamic method analysis beforetriedResolver = NO, then perform dynamic method analysis;
在这里插入图片描述

dynamic method analysis phase will be based on whether the object ismeta -class or class, distinguish calling different methods:
Non -meta -class object (object method) call:+(BOOL)resolveInstanceMethod:(SEL)sel
meta -class object (class method) call:+(BOOL)resolveClassMethod:(SEL)sel

Lift a chestnut:

YZPerson.h
#import <Foundation/Foundation.h>
@interface YZPerson : NSObject
- (void)run;
@end

YZPerson.m
#import "YZPerson.h"
@interface YZPerson()
@end
@implementation YZPerson
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    
    NSLog(@"%s", __func__);
    return [super resolveInstanceMethod:sel];
}
@end

YZPerson *person = [[YZPerson alloc] init];
[person run];

在这里插入图片描述Instructions,When you can’t find the RUN method, the ResolveInstanceMethod method is called.

The above example is only explained or proved that when the RUN method cannot be found, the ResolveInstanceMethod method is called.

can be used to add dynamic addition:
在这里插入图片描述
After dynamic adding methods, continue to execute the followinggoto retry, that is, continue to execute message sending
Since the method has been dynamically added, the method is added to the method list in the current class. This time I found the Method and then executed normally.

dynamic method analysis process:
在这里插入图片描述

Dynamic method Analysis of the most important role and method is to add a method through dynamic, and the message can continue to send execution

When the message sending and dynamic method analysis process, no method name is found, it will enter the third stage:Message forwarding. which isThe method that you can’t deal with it to other objects

mainly calls__forwarding__method, the following two methods will be called inside:
The main method used is:
- (id)forwardingTargetForSelector:(SEL)aSelector
+ (id)forwardingTargetForSelector:(SEL)aSelector
Among them, the return value is forwarded to other objects. The parameter is a method name that cannot be handled.

For example:

YZCat.M file#import "YZCat.h"

@implementation YZCat
- (void)run
{
    
    NSLog(@"%s", __func__);
}
@end


YZPerson.M file#import "YZPerson.h"
#import <objc/message.h>
#import "YZCat.h"
@interface YZPerson()
@end
@implementation YZPerson
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    
    if (aSelector == @selector(run)) {
    
        return [[YZCat alloc] init];// Return to the object that can be processed
		// The code of the previous sentence is equivalent to execution
		//return objc_msgSend([[YZCat alloc] init], aSelector);
    }
    return [super forwardingTargetForSelector:aSelector];
}@end 

 operation result:2020-05-23 12:16:49.554329+0800Runtime Learning[8510:5105879] -[YZCat run]

can be seen that when the current two stages are unable to deal with, the third stage can be processed according to the message.

codereturn [[YZCat alloc] init];When it is executedreturn objc_msgSend([[YZCat alloc] init], aSelector);

If
- (id)forwardingTargetForSelector:(SEL)aSelectoror
+ (id)forwardingTargetForSelector:(SEL)aSelector
The two methods are not executed or returned to NIL, the system will call
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelectormethod, return a method signature.

The purpose of the

method is: get the return value type, parameter type

If the signature method is not implemented, the error that can be reported cannot be found

在这里插入图片描述If the method signature is implemented, you need to call- (void)forwardInvocation:(NSInvocation *)anInvocationfunction.

The purpose is:Sign the method and encapsulate it in Invocation

The specific implementation is as follows:

在这里插入图片描述
In FORAWARDINVOCATION, you can do things at will. Even if you do n’t write anything, you only have the method of signing the method.

Message forwarding process:
在这里插入图片描述In addition to the object method, you can do message forwarding, and the class method can also be reposted.

+ (id)forwardingTargetForSelector:(SEL)aSelector
{
    
    if (aSelector == @selector(eat)) {
    
        return [YZCat class];
    }
    return [super forwardingTargetForSelector:aSelector];
}result:2020-05-24 11:02:53.707519+0800Runtime Learning[11653:5343993] +[YZCat eat]

What needs to be noticed is:When the message is forwarded, even if the original call method is a class method, you can forward it to an object method.

[YZPerson eat];// Class method call

YZPerson.M file+ (id)forwardingTargetForSelector:(SEL)aSelector
{
    
    if (aSelector == @selector(eat)) {
    
        return [[YZCat alloc] init];// forward to the object method
    }
    return [super forwardingTargetForSelector:aSelector];
}

YZCat.M file+ (void)eat
{
    
    NSLog(@"%s", __func__);
}

- (void)eat
{
    
    NSLog(@"%s", __func__);
}result:2020-05-24 11:11:16.169383+0800Runtime Learning[11852:5352885] -[YZCat eat]

dynamic method analysis
1. Call ResolveInstanceMethod: Method. Allow users to dynamically add implementation for the Class at this time. If it is implemented, call and return to YES and restart the objc_msgsend process. The object will respond to this selector this time because it has called Class_ADDMETHOD. If it is not realized, continue the following actions.

Message forwarding
2, call ForwardingTargetForselector: method, try to find an object that can sound the message. If you get it, forward the message directly to it and return to the non -Nil object. Otherwise, return Nil and continue the following action. Note that do not return to Self here, otherwise it will form a dead cycle.

3. Call MethodSignatureForselector: method, try to get a method signature. If you can’t get it, call the DOESNOTRCOGNIZESELECTOR directly to throw an exception. If you can get it, return to non -nil; passed to a nsinvocation and passed to Forwardinvocation:.

4. Call forwardinVocation: Method, and sign the method obtained in the third step to sign it into infitation. How to deal with it is here and return to non -Nil.

5. Call DOESNOTRCOGNIGESELECTOR:, the default implementation is to throw an exception. If the third step fails to get a method signature, execute this step.


@property (assign, nonatomic) int age;

This code, did for us: SET/GET method declaration, set/get method implementation, generating member variables

  1. set/get method declaration
- (void)setAge:(int)age;
- (int)age;
  1. set/get method implementation
- (void)setAge:(int)age
{
    
    _age = age;
}

- (int)age
{
    
    return _age;
}
  1. Generate member variables
    {

    _age;
    }

When we use it

@property (assign, nonatomic) int age;//.h
@dynamic age;//.m

YZPerson *person = [[YZPerson alloc] init];
person.age = 10;
NSLog(@"%d", person.age);

Last reportreason: '-[YZPerson setAge:]: unrecognized selector sent to instance 0x100745230'error.
This is because, use@dynamic age;will not automatically generate the implementation of Age’s Setter/Getter method, nor will it automatically generate member variables (IVAR).
It should be noted that the statement of the Setter/Getter method of Age is not affected.

So we can use@dynamic age;to achieve the implementation of the Setter/Getter method during the operation, such as:

.h File 
 @ @property (assign, nonatomic) int age;

.M file 
 @Dynamic Age;

void setAge(id self, SEL _cmd, int age)
{
    
    NSLog(@"age is %d", age);
}

int age(id self, SEL _cmd)
{
    
    return 100;
}

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    
    NSLog(@"%s", __func__);
    if (sel == @selector(setAge:)) {
    
        class_addMethod(self, sel, (IMP)setAge,
                        "[email protected]:i");
        return YES;
    }else if (sel == @selector(age)) {
    
        class_addMethod(self, sel, (IMP)age,
                        "[email protected]:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}call 
 Yzperson*person = [[YZPerson alloc] init];
person.age = 10;
NSLog(@"%d", person.age);Run results:2020-05-24 12:45:36.960569+0800Runtime Learning[12661:5405426] +[YZPerson resolveInstanceMethod:]
2020-05-24 12:45:36.961027+0800Runtime Learning[12661:5405426] age is 10
2020-05-24 12:45:36.961090+0800Runtime Learning[12661:5405426] +[YZPerson resolveInstanceMethod:]
2020-05-24 12:45:36.961163+0800Runtime Learning[12661:5405426] 100

source

Random Posts

Memory Learning 2

BOOTSTRAP about Checkbox’s selection that cannot be achieved It is not applicable here.

MFC+OpenCV3.3.1+Display image video+water level recognition

c language signal

Moors Transfer Information