Why did I write this article

Today, I encountered a problem when WRITING Bugly error reporting. Since the project uses the componentized development idea, it is necessary to know whether the Bugly component is imported in the error reporting component. If there is no Bugly component, Bugly will not be reported. My first thought of use to judge whether there is Bugly NSClassFromString, if present, and then use the performSelector: withObject call Bugly reporting method at runtime, BuglyLog level:<#(BuglyLogLevel)#> log:<#(NSString *),… # > the first parameter to the incoming NSUInterger type of basic data, but the performSelector: withObject method can only be passed OC object, so I am at the same time helping to study to solve the problem of the OC execution method, several common methods of messaging.

The first way

PerformSelector way

This should be developed in the process of dynamic execution method is the most common way, the most common usage is performSelector: withObject, or you can take a parameter performSelector: withObject: withObject:, call like this:

id BuglyClass = (id)NSClassFromString(@"Bugly");
NSError *error = [NSError errorWithDomain:errorTitle code:- 1 userInfo:@{NSLocalizedDescriptionKey:@ ""}];
[BuglyClass performSelector:@selector(reportError:) withObject:error];
Copy the code

There is also the use of delayed triggering

performSelector:withObject:afterDelay:
Copy the code

The use of execution in a thread, of course, is not the focus of this article, so it is covered briefly

- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg - (void)performSelectorOnMainThread:(SEL)aSelector  withObject:(id)argwaitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
Copy the code
And it’s not hard to see that performSelector has some drawbacks

1. Cannot pass more than three parameters. 2

The second way

NSInvocation way

Okay, let’s get right to the code

NSString *string = @"test";
NSNumber *number = [NSNumber numberWithInt:1];

SEL selector = @selector(function2:count:); NSMethodSignature *signature = [self methodSignatureForSelector:selector]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; invocation.target = self; invocation.selector = selector; The first and second arguments are target and selector [Invocation]setArgument:&string atIndex:2];
[invocation setArgument:&number atIndex:3]; // Invoke [Invocation];Copy the code
  • NSMethodSignature: Method signature, if the method doesn’t exist or parameters on, will directly crach, actually I don’t really understand the meaning of the signature, might be enough code, according to my understanding, the digital signature is like find out this target, whether to have the method, parameters are correct, if all right, then signature to take effect, otherwise it may collapse
  • NSInvocation: wraps all the contents of a message delivery, including target, select, argument, and so on, and finds the target to send the message at runtime
  • Note that the parameter passed here is the address, so it does not satisfy our requirements

In order to solve the problem reported to Bugly today, I have to continue to explore

In the search… In the search… In the search… We found a solution!

The third way

Function pointer mode

In the code

id target = self;
SEL selector = @selector(function:count:); // The first second argument is self and selector typedef void (*function)(id, SEL, NSString *, int);
function methodToCall = (function)[target methodForSelector:selector];
methodToCall(target, selector, @"string", 1);Copy the code

Now, for those of you who haven’t seen this before, I didn’t know it until I was solving the Bugly problem, but I think you can see it in the code, you find the method that you want to call with a selector, you define a pointer to a function that points to the method that you select, and then you call that function, and I tried it out, It can perfectly solve the problem I met at the beginning of the article

Continue to explore

The fourth way

The lowest level of messaging

In the code

id BuglyLogClass = (id)NSClassFromString(@"BuglyLog");
SEL selector = @selector(level:log:);
void (*logAction)(id, SEL, NSUInteger,NSString *) = (void (*)(id, SEL, NSUInteger,NSString *))objc_msgSend;
logAction(BuglyLogClass, selector, 1,@"description");
Copy the code

This is the last method I used. All target execution methods are implemented at the bottom through the objc_msgSend message passing, which boils down to a message sent to the target target. Let’s take a look at Apple’s official explanation

/* Basic Messaging Primitives
 *
 * On some architectures, use objc_msgSend_stret for some struct return types.
 * On some architectures, use objc_msgSend_fpret for some float return types.
 * On some architectures, use objc_msgSend_fp2ret for some float return types.
 *
 * These functions must be cast to an appropriate function pointer type 
 * before being called. 
 */
void objc_msgSend(void /* id self, SEL op, ... */ )
Copy the code

The underlying implementation of performSelector actually calls objc_msgSend to send a message

- (id)performSelector:(SEL)sel {
    if(! sel) [self doesNotRecognizeSelector:sel];return ((id(*)(id, SEL))objc_msgSend)(self, sel);
}
Copy the code

Study here, I have solved my Bugly pass parameter report log problem, during this period learned a lot of knowledge, may write some is not very correct, hope Daniu correction.