今天在写Bugly上报错误的时候遇到了一个问题,因为项目使用组件化开发思想,在上报错误组件里须要知道当前是否导入了Bugly组件,若是没有Bugly组件,则不进行Bugly上报,我第一个想到了用
NSClassFromString
来判断是否存在Bugly,若是存在,再使用performSelector:withObject
在runtime
调用Bugly上报方法,可是Bugly输出日志的方法BuglyLog level:<#(BuglyLogLevel)#> log:<#(NSString *), ...#>
第一个参数须要传入NSUInterger
类型的基本数据,可是performSelector:withObject
方法只能传入OC对象,因此我在解决问题的同时顺带研究了一下OC执行方法,消息传递的几种常见方法。bash
这应该是开发过程当中动态执行方法最经常使用的方式了吧,最多见的用法就是performSelector:withObject
,或者能够再带一个参数performSelector:withObject:withObject:
,就像这样调用:app
id BuglyClass = (id)NSClassFromString(@"Bugly");
NSError *error = [NSError errorWithDomain:errorTitle code:-1 userInfo:@{NSLocalizedDescriptionKey:@""}];
[BuglyClass performSelector:@selector(reportError:) withObject:error];
复制代码
也有延时触发的用法函数
performSelector:withObject:afterDelay:
复制代码
在某个线程中执行的用法,固然这些不是本文讨论的重点,因此一笔带过组件化
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
复制代码
1.不能传超过三个参数 2.参数只能为OC对象ui
先不说啥,直接上代码spa
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;
// 第一和第二个参数是target和selector
[invocation setArgument:&string atIndex:2];
[invocation setArgument:&number atIndex:3];
//执行
[invocation invoke];
复制代码
NSMethodSignature
: 方法签名,若是方法不存在或者参数对不上,会直接crach,其实我也不是很理解这个签名的意思,多是代码写得不够多吧,按照个人理解,这个数字签名就像是找一下这个target里面是否有这个方法,参数是否正确,若是都对,则签名生效,不然就可能奔溃NSInvocation
: 包装了一次消息传递的全部内容,包括target,select,argument等等,会在运行时找到目标发送消息- 注意这里传的参数是地址,因此还不不知足咱们的需求
搜索中... 搜索中... 搜索中... 找到方案了!线程
上代码指针
id target = self;
SEL selector = @selector(function:count:);
// 第一个第二个参数是self和selector
typedef void (*function)(id, SEL, NSString *, int);
function methodToCall = (function)[target methodForSelector:selector];
methodToCall(target, selector, @"string",1);
复制代码
可能有些小伙伴没有见过这种用法,我其实也是解决Bugly问题的时候才了解到的,不过看代码应该是能看懂的,首先用selector找到要调用的方法,再定义一个函数指针,指向selector选择的方法,而后调用这个函数执行,我试了下,能完美解决文章开头我遇到的问题日志
上代码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");
复制代码
这是我最后使用的方法。全部target执行方法在底层都是经过objc_msgSend
消息传递实现的,要执行的方法归根结底就是一条消息,发送给目标target,咱们来看一下Apple官方的解释
/* 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, ... */ )
复制代码
其实performSelector的底层实现也是调用了objc_msgSend实现消息发送
- (id)performSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL))objc_msgSend)(self, sel);
}
复制代码
研究到这里,我已经解决了个人Bugly传参数上报日志的问题,在此期间学到了不少知识,可能有些写的不是很正确,望大牛指正。