This is the sixth day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

1. Crash

APP crashes are an important part of the APP experience, and crash location is often a headache for developers. The occurrence of a Crash means that some operations violate code rules. Common types of Crash are as follows:

  • The container of crossing the line
  • Use uninitialized variables
  • User Authorization
  • The selector method is not defined
  • Child threads refresh the UI
  • KVO
  • Data type mismatch
  • Out of memory
  • Wild pointer
  • Infinite loop

How to deal with Crash efficiently?

2. Selector method undefined crash

The following code will obviously crash because there is no logicEdu method.

#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super  viewDidLoad]; // Do any additional setup after loading the view. } - (IBAction)btnAction:(UIButton *)sender { [sender performSelector:@selector(logicEdu:)]; } @endCopy the code

So adding the following method still crashes.

- (void) logicEdu:(UIButton *)sender {
    
}
Copy the code

First take a look at the stack. Exception_throw here means exception is thrown, and throws an exception is + [NSObject (NSObject) instanceMethodSignatureForSelector:] is slowly forward. And when you send a message, it finds the class through the object’s ISA and it looks for the selector in the list of methods of that class, and if it doesn’t find it, it looks in the parent class of that class, until it finds NSObject and it doesn’t find it then it goes through the message forwarding process,

There are three remedial opportunities in the message forwarding process:

  • Dynamic method resolution (add method)
  • Fast message forwarding (forward to another object for processing)
  • Slow message forwarding (dynamic signature sign an SEL)

If all three are not handled, then it crashes. Here to create an NSObject classification for fast forward – forwardingTargetForSelector;

@implementation NSObject (method)
- (id)forwardingTargetForSelector:(SEL)aSelector {
    id result = [self forwardingTargetForSelector:aSelector];
    return result;
}
@end
Copy the code

A warning will be issued because the classification method name is the same as the method name of the main class, so you need to use method-Swizzing imp swap. This is still crashed when I run it. Method-swizzing is usually implemented in load, which is called before main and is actively called. However, writing functions in load affects startup speed, so you should try not to write time-consuming operations in load. Classes and classes are lazily loaded, but if load is implemented, classes are loaded before main. The startup speed is affected because there are more methods called at the bottom such as attchCategory. Some write method swaps inside initialize, which is fine, since initialize is called the first time the method is called. However, it is not recommended to exchange methods in Initialize because methods may not be called.

+ (void)load {
 
    Method originalMethod = class_getInstanceMethod(self, @selector(forwardingTargetForSelector:));
    Method swizzleMethod = class_getInstanceMethod(self, @selector(LSForwardingTargetForSelector:));
    method_exchangeImplementations(originalMethod, swizzleMethod);
}
Copy the code

Then create a ForwardingTarget class to override the message forwarding method.

id newDynamicMethod(id self,SEL _cmd){
    return [NSNull null];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    class_addMethod(self.class, sel, (IMP) newDynamicMethod, "@@:");
    [super resolveInstanceMethod:sel];
    return YES;
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    id result = [super forwardingTargetForSelector:aSelector];
    return result;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    id result = [super methodSignatureForSelector:aSelector];
    return result;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    [super forwardInvocation:anInvocation];
}

Copy the code

Add a static variable target to the class and initialize it inside load.

static ForwardingTarget *target = nil;
Copy the code

In this way, after running, clicking button will not crash. Here, we can also print sel of crash in newDynamicMethod.

id newDynamicMethod(id self,SEL _cmd){
    NSLog(@"%@",NSStringFromSelector(_cmd));
    return [NSNull null];
}
Copy the code