iOS开发之NSMethodSignature(方法签名)

OC中方法调用有三种:

第一种:直接调用

- (void)viewDidLoad {
    [super viewDidLoad];
  
    [self printStr1:@"hello"];
}

- (void)printStr1:(NSString*)str{
    NSLog(@"printStr1  %@",str);
}

第二种:通过performSelector

- (void)viewDidLoad {
    [super viewDidLoad];
  
    [self performSelector:@selector(printStr1:) withObject:@"hello world"];
}

- (void)printStr1:(NSString*)str{
    NSLog(@"printStr1  %@",str);
}

第三种:NSMethodSignature 和NSInvocation

//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@😡”就代表:

  1. “v”:代表返回值void
  2. “@”:代表一个对象,这里指代的id类型zhangsan,也就是消息的receiver
  3. “:”:代表SEL
  4. “@”:代表参数lisi

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则调用相应方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Objective-C中,可以通过运行时库提供的方法来获取函数名。常用的方法有以下几种: 1. 使用SEL获取函数名 在Objective-C中,每个方法都对应一个SEL类型的唯一标识符,可以通过这个标识符来获取方法名。具体实现方式如下: ```objective-c - (void)testMethod { SEL sel = @selector(testMethod); const char *methodName = sel_getName(sel); NSLog(@"方法名为:%s", methodName); } ``` 2. 使用NSMethodSignature获取函数名 NSMethodSignature是一个类,用于表示方法的参数类型和返回值类型。可以通过它的方法获取方法的名称。具体实现方式如下: ```objective-c - (void)testMethod { NSMethodSignature *signature = [self methodSignatureForSelector:_cmd]; const char *methodName = [signature name]; NSLog(@"方法名为:%s", methodName); } ``` 3. 使用class_copyMethodList获取函数名 class_copyMethodList是一个函数,用于获取一个类的所有方法。可以通过遍历方法列表来获取对应的方法名。具体实现方式如下: ```objective-c - (void)testMethod { unsigned int methodCount = 0; Method *methodList = class_copyMethodList([self class], &methodCount); for (int i = 0; i < methodCount; i++) { Method method = methodList[i]; const char *methodName = sel_getName(method_getName(method)); NSLog(@"方法名为:%s", methodName); } free(methodList); } ``` 需要注意的是,这种方式只能获取当前类的方法名,无法获取父类的方法名。如果需要获取父类的方法名,可以使用class_copyMethodList函数遍历父类的方法列表。 以上是Objective-C中通过运行时方法获得函数名的常用方法,还有其他一些方法,具体可以查看Objective-C运行时库提供的API文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值