本文共 4767 字,大约阅读时间需要 15 分钟。
OC中方法调用有三种:
- (void)viewDidLoad { [super viewDidLoad]; [self printStr1:@"hello"];}- (void)printStr1:(NSString*)str{ NSLog(@"printStr1 %@",str);}
- (void)viewDidLoad { [super viewDidLoad]; [self performSelector:@selector(printStr1:) withObject:@"hello world"];}- (void)printStr1:(NSString*)str{ NSLog(@"printStr1 %@",str);}
//1.获取方法签名 NSMethodSignature *sigOfPrintStr = [self methodSignatureForSelector:@selector(printStr1:)]; //2.获取方法签名对应的invocation NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sigOfPrintStr]; /** 3.设置消息接受者,与[invocationOfPrintStr setArgument:(__bridge void * _Nonnull)(self) atIndex:0]等价 */ [invocation setTarget:self]; /**4.设置要执行的selector。与[invocationOfPrintStr setArgument:@selector(printStr1:) atIndex:1] 等价*/ [invocation setSelector:@selector(printStr1:)]; //5.设置参数 NSString *str = @"hello world"; [invocation setArgument:&str atIndex:2]; //开始执行 [invocation invoke];
NSMethodSignature概述
NSMethodSignature和NSInvocation是Foundation框架为咱们提供的一种调用方法的方式,常常用于消息转发。 NSMethodSignature用于描述method的类型信息:返回值类型,及每一个参数的类型。 能够经过下面的方式进行建立:创建方法有三种:
@interface NSObject //获取实例方法的签名 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; //获取类方法的签名 + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector; @end
//使用ObjCTypes建立方法签名 @interface NSMethodSignature + (nullable NSMethodSignature *)signatureWithObjCTypes:(const char *)types; @end
其实ObjcTypes就是 “v@😡”。它是一个是字符串数组,该数组包含了方法的类型编码。
比如有下面这样一个方法:- (void)goToSchoolWithPerson:(Person *)person; [zhangsan goToSchoolWithPerson:lisi];
我们都知道消息发送会被转换成objc _ msgSend(id reciever,SEL sel,prarams1,params2,…)。所以上面的方法会被转换成:
void objc_msgSend(zhangsan,@selector(goToSchoolWithPerson:),lisi); //包含两个隐藏参数
这里的 “v@😡”就代表:
NSMethodSignature的一种应用
数据模型解析中对null数据的处理
#import "NSNull+BCMNull.h"@implementation NSNull (BCMNull)- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];#ifdef DEBUG //抛出错误调用栈信息 NSLog(@"=========NSNull Message=================\n%@", [NSThread callStackSymbols]);#else#endif NSArray *someClass = @[[NSMutableArray class], [NSMutableString class], [NSMutableDictionary class], [NSDate class], [NSNumber class], [NSData class]]; ///> 如果签名方法不存在,则生成一个签名方法 if (!methodSignature) { for (Class class in someClass) { if ([class instancesRespondToSelector:aSelector]) { methodSignature = [class instanceMethodSignatureForSelector:aSelector]; } } } return methodSignature;}- (void)forwardInvocation:(NSInvocation *)anInvocation { anInvocation.target = nil; [anInvocation invoke];}@end
举例:
建立一个TestModel类,只声明testMethod,而不去实现,如果TestModel对象调用testMethod时会crash。 如果不想crash,就必须实现methodSignatureForSelector和forwardInvocation,在methodSignatureForSelector方法里面返回一个对应的方法签名。#import "TestModel.h"@implementation TestModel- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { if(aSelector == @selector(testMethod)) { return [NSMethodSignature signatureWithObjCTypes:"v@:"]; } return nil;} -(void)forwardInvocation:(NSInvocation *)anInvocation{ }-(void)testMethod2 { NSLog(@"调用了方法2");}@end
当然你也可以在forwardInvocation方法里面写一些其他内容,
新建一个TestModelHelper1类,声明且实现testMethod方法:#import "TestModelHelper1.h"@implementation TestModelHelper1-(void)testMethod{ NSLog(@"i am TestModelHelper1");} @end
在forwardInvocation里面增加如下代码:
#import "TestModel.h"#import "TestModelHelper1.h"@implementation TestModel- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { if(aSelector == @selector(testMethod)) { return [NSMethodSignature signatureWithObjCTypes:"v@:"]; } return nil;} -(void)forwardInvocation:(NSInvocation *)anInvocation{ if (anInvocation.selector == @selector(testMethod)) { TestModelHelper1 *h1 = [[TestModelHelper1 alloc] init]; [anInvocation invokeWithTarget:h1]; }}-(void)testMethod2 { NSLog(@"调用了方法2");}@end
控制台输出:
2021-05-21 13:46:25.716145+0800 TestMethodSignature[12594:313045] i am TestModelHelper1
由此,我们得出方法调用顺序:
向一个实例发送一个消息后,系统是处理的流程: 1.系统会check是否能response这个消息,如果没有,系统就会发出methodSignatureForSelector消息,寻问它这个消息是否有效?有效就返回对应的方法地址之类的,无效则返回nil。如果是nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了. 如果不是nil接着发送forwardInvocation消息。 2.如果能response则调用相应方法。转载地址:http://cwcti.baihongyu.com/