iOS -- message forwarding mechanism

iOS – message forwarding mechanism

I believe you are familiar with this sentence unrecognized selector sent to instance 0x ******************************************************************.

Let's briefly talk about saving the code about to crash - iOS message forwarding

Dynamic binding

Because OC is a dynamic runtime language, one of its features is dynamic binding.

Regarding dynamic binding, the explanation given on Apple's official website is: (determining the method to invoke at runtime).

To put it simply: the program can't determine the actual method to call until it executes.

This creates a problem. I can send a message to an instance to execute a method that does not belong to me. unrecognized selector sent to instance will appear at this time.

If this happens, we can apply message forwarding to solve this problem. Turn this method that does not belong to you into your own method, or find an instance with this method to execute this method.

Save code that is about to crash

Step 1: Method Analysis and processing stage | dynamic method resolution

Within this method, you can dynamically add methods for the current class.

Point the method implementation of sel to an existing method

/**
 Method analysis processing stage | dynamic method resolution
 Within this method, you can dynamically add methods for the current class.
 Point the method implementation of sel to an existing method
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    NSString *selectorString = NSStringFromSelector(sel);
    printf("%s %s\n", __func__, selectorString.UTF8String);
    
    // Get the instance method of class according to sel
    Method method = class_getInstanceMethod([self class], @selector(dynamic_method));
    // Get the function pointer of class according to sel
    IMP method_imp = class_getMethodImplementation([self class], @selector(dynamic_method));
    // Add an implementation to sel that cannot be found
    BOOL ret = class_addMethod([self class], sel, method_imp, method_getTypeEncoding(method));
    printf("%s\n", ret?"Exchange added successfully":"Exchange add failed");
    // The returned result does not affect the process
    return YES;
}

Step 2 fast forwarding stage | fast search

Triggered when the problem is not solved in the previous step.

Return an instance that can respond to aSelector, that is, forward aSelector to another class.

/**
 Fast forwarding stage | fast search
 Triggered when the problem is not solved in the previous step.
 Return an instance that can respond to aSelector, that is, forward aSelector to another class.
 */
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    NSString *selectorString = NSStringFromSelector(aSelector);
    printf("%s %s\n", __func__, selectorString.UTF8String);
    
    if ([selectorString isEqualToString:@"no_imp_method"]) {
        // Returns an instance that implements the aSelector function
        // If the instance does not implement aSelector, go to the next step, methodSignatureForSelector
        printf("%s Forward message to BackUpClass\n",__func__);
        return  [[BackUpClass alloc] init];
    }
    // If self or nil is returned, it indicates that there is no target that can respond. Then go to the next step, methodSignatureForSelector.
    return nil;
}

Step 3: normal forwarding stage | slow lookup

Get a method signature. The signature is generated by an instance that responds to aSelector.
If there is a signature, the last step of message forwarding is forwardInvocation.

/**
 General forwarding phase | slow lookup
 Returns a method signature. The signature is generated by an instance that responds to aSelector.
 
 */
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSString *selectorString = NSStringFromSelector(aSelector);
    printf("%s %s\n", __func__, selectorString.UTF8String);
    
    BackUpClass * backUp = [BackUpClass new];
    NSMethodSignature * sign = [backUp methodSignatureForSelector:aSelector];
    //If there is a signature, the last step of message forwarding is forwardInvocation
    return sign;
}

You can also do nothing. So far, the message forwarding is over and there will be no crash.

/**
 Forward sel to an object that actually implements sel
 You can also do nothing. So far, the message forwarding is over and there will be no crash.
 */
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    printf("%s %s\n",__func__ , anInvocation.description.UTF8String);
    
    // Create alternate message receiving object
    BackUpClass * backUp = [[BackUpClass alloc] init];
    printf("%s Forward message to BackUpClass\n",__func__);
    [anInvocation invokeWithTarget:backUp];
}

Write it at the back

Talk about. Can communicate.

Tags: iOS

Posted on Mon, 18 Oct 2021 19:47:19 -0400 by azylka