简介:
OC中方法调用分为四种方式,如下:
- OC代码调用;
- NSObject的performSelector调用;
- NSInvocation调用;
- objc_msgSend即runtime底层方法调用。
我们最熟悉的当然1和2,我们经常用到,是OC层面上的调用;而3涉及到了方法的动态解析和消息转发机制;4中objc_msgSend调用,是runtime层方法的调用,其实上述的1、2、3到底层runtime层,都是通过objc_msgSend进行调用的,也就是OC的消息发送机制。该篇文章不讲述其它,只是简单对方法调用进行记录和说明。
OC代码调用:
// .h文件
@interface ZBYRunTimePersionModel : NSObject
/// 消息调用的四种方法
- (NSString *)callTestWithStr1:(NSString *)str1 str2:(NSString *)str2 str3:(NSString *)str3;
@end
// .m文件
@implementation ZBYRunTimePersionModel
- (NSString *)callTestWithStr1:(NSString *)str1 str2:(NSString *)str2 str3:(NSString *)str3 {
NSString * result = [NSString stringWithFormat:@"%@-%@-%@", str1, str2, str3];
return result;
}
@end
// OC代码直接调用
ZBYRunTimePersionModel * person = [ZBYRunTimePersionModel new];
NSString * r1 = [person callTestWithStr1:@"a" str2:@"b" str3:@"c"];
NSLog(@"%@", r1);
最常用的方式,直接调用,缺点不能通过方法名字符串来执行方法。
NSObject的performSelector调用:
// 2.1用方法调用
NSString * r21 = [person performSelector:@selector(callTestWithStr1:str2:str3:) withObject:@"a" withObject:@"b"];
NSLog(@"%@", r21);
// 2.2用方法对应的字符串初始化为方法,再进行调用
SEL selected = NSSelectorFromString(@"callTestWithStr1:str2:str3:");
NSString * r22 = [person performSelector:selected withObject:@"a" withObject:@"b"];
NSLog(@"%@", r22);
通过NSObject继承的底层方法进行调用;无法进行2个以上参数的传递。
NSInvocation调用:
/**
3.NSInvocation调用方法
3.1进行方法签名;NSMethodSignature的两个参数:numberOfArguments方法参数的个数;methodReturnLength方法返回值类型
的长度,大于0表示有返回值
3.2初始化NSInvocation并设置参数
第一个参数下标为0是target,及响应者;
第二个参数是selector,及需要调用的方法;
第三个起是自定义参数,必须传递参数的地址,不能直接传值,例如:str1,str2,str3
3.3获取返回值;可以在调用invoke前,也可以在invoke之后
*/
NSMethodSignature * sign = [person methodSignatureForSelector:selected];
// 或者
// NSMethodSignature * sign = [[self class] instanceMethodSignatureForSelector:selected];
NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:sign];
invocation.target = person;
invocation.selector = selected;
NSString *arg1 = @"a";
NSString *arg2 = @"b";
NSString *arg3 = @"c";
[invocation setArgument:&arg1 atIndex:2];
[invocation setArgument:&arg2 atIndex:3];
[invocation setArgument:&arg3 atIndex:4];
[invocation invoke];
if (sign.methodReturnLength > 0) {
NSString * result = nil;
[invocation getReturnValue:&result];
NSLog(@"%@", result);
}
else {
NSLog(@"没有返回值")
}
需要对方法进行签名。
objc_msgSend即runtime底层方法调用:
// 4.第四种方法:底层调用,方法到底层都是通过objc_msgSend调用的,直接用objc_msgSend强制转换进行调用
// 4.1强制转换和调用分开
void *(*zby_runtime_msgObserverSend)(id, SEL, void *, void *, void *) = (void *)objc_msgSend;
NSString * r = (__bridge NSString *)(zby_runtime_msgObserverSend(person, selected, @"a", @"b", @"c"));
NSLog(@"%@", r);
// 4.2强制转换和调用一起
((void(*)(id, SEL, void *, void *, void *))objc_msgSend)(person, selected, @"a", @"b", @"c");
// NSString * r2 = (__bridge NSString *)((void(*)(id, SEL, void *, void *, void *))objc_msgSend)(person, selected, @"a", @"b", @"c");
// NSLog(@"%@", r2);