// 获取实例方法 - (void)getMethods { int outCount = 0; Person *p = [Person new]; Method *methods = class_copyMethodList([p class], &outCount); for (int i = 0; i < outCount; i ++) { NSLog(@"=============%d", i); // 获取方法名 Method method = methods[i]; SEL methodName = method_getName(method); NSLog(@"方法名= %@", NSStringFromSelector(methodName)); // 获取参数 char argInfo[512] = {}; unsigned int argCount = method_getNumberOfArguments(method); for (int j = 0; j < argCount; j ++) { // 参数类型 method_getArgumentType(method, j, argInfo, 512); NSLog(@"参数类型= %s", argInfo); memset(argInfo, '\0', strlen(argInfo)); } // 获取方法返回值类型 char retType[512] = {}; method_getReturnType(method, retType, 512); NSLog(@"返回类型值类型= %s", retType); } free(methods); }
调用方式:html
1. 使用 performSelector 下面2和3行结果同样。这样比使用对象直接调用好处是编译器不会报错,也不用方法暴露头文件。函数
1 Person *p = [Person new]; 2 [p performSelector:@selector(eat)]; // log: xxx eat==== 3 [p performSelector:NSSelectorFromString(@"eat")]; // log: xxx eat====
2. 使用 objc_msgSend ,对比上面 objc_msgSend 好处是传递多个参数时更为方便code
Person *p = [Person new]; // 须要引用 Person.h 头文件 objc_msgSend(p, @selector(eat:str2:str3:), @"1", @"2", @"3"); // log: eat====1==2==3
3. 利用函数实现IMP,IMP类型结构与objc_msgSend底层是同一类型,与2中实现无差异orm
Person *p = [Person new]; IMP imp = [p methodForSelector:@selector(eat)]; void (* tempFunc)(id target, SEL) = (void *)imp; tempFunc(p, @selector(eat)); // log: xxx eat====
4. 使用类对象发送消息,上面3种方式都须要引用 Person.h 头文件,这里类对象进行调用能够解决这个问题htm
Class pClassObj = NSClassFromString(@"Person"); objc_msgSend([pClassObj new], @selector(eat));
5.多参数对象
-(id)glt_performSelector:(SEL)selector withObject:(id)object,...NS_REQUIRES_NIL_TERMINATION;
{
//根据类名以及SEL 获取方法签名的实例
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
if (signature == nil) {
NSLog(@"--- 使用实例方法调用 为nil ---");
signature = [self methodSignatureForSelector:selector];
if (signature == nil) {
NSLog(@"使用类方法调用 也为nil, 此时return");
return nil;
}
}
//NSInvocation是一个消息调用类,它包含了全部OC消息的成分:target、selector、参数以及返回值。
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = selector;
NSUInteger argCount = signature.numberOfArguments;
// 参数必须从第2个索引开始,由于前两个已经被target和selector使用
argCount = argCount > 2 ? argCount - 2 : 0;
NSMutableArray *objs = [NSMutableArray arrayWithCapacity:0];
if (object) {
[objs addObject:object];
va_list args;
va_start(args, object);
while ((object = va_arg(args, id))){
[objs addObject:object];
}
va_end(args);
}
if (objs.count != argCount){
NSLog(@"--- objs.count != argCount! please check it! ---");
return nil;
}
//设置参数列表
for (NSInteger i = 0; i < objs.count; i++) {
id obj = objs[i];
if ([obj isKindOfClass:[NSNull class]]) {
continue;
}
[invocation setArgument:&obj atIndex:i+2];
}
[invocation invoke];
//获取返回值
id returnValue = nil;
if (signature.methodReturnLength != 0 && signature.methodReturnLength) {
[invocation getReturnValue:&signature];
}
return returnValue;
}
索引
注:ci
TestClass *cls = [[TestClass alloc] init];
objc_msgSend(cls, @selector(fun)); //错误写法(arm64崩溃偶尔发生)
((void (*)(id, SEL))objc_msgSend)(cls, @selector(fun)); //正确写法get
//eg:编译器
void (*glt_msgsend)(id, SEL, NSString *, NSString *) = (void (*)(id, SEL, NSString *, NSString *))objc_msgSend; glt_msgsend(cls, @selector(eat:say:), @"123", @"456");