获取block的方法签名,从而获取block的参数和返回值类型

首先我们要了解block的真实结构如下:参考:这里

 1 struct ZBBlockLiteral {
 2     void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
 3     int flags;
 4     int reserved;
 5     void (*invoke)(void *, ...);
 6     struct block_descriptor {
 7         unsigned long int reserved;    // NULL
 8         unsigned long int size;         // sizeof(struct Block_literal_1)
 9         // optional helper functions
10         void (*copy_helper)(void *dst, void *src);     // IFF (1<<25)
11         void (*dispose_helper)(void *src);             // IFF (1<<25)
12         // required ABI.2010.3.16
13         const char *signature;                         // IFF (1<<30)
14     } *descriptor;
15     // imported variables
16 };
17 
18 // flags enum
19 enum {
20     ZBBlockDescriptionFlagsHasCopyDispose = (1 << 25),
21     ZBBlockDescriptionFlagsHasCtor = (1 << 26), // helpers have C++ code
22     ZBBlockDescriptionFlagsIsGlobal = (1 << 28),
23     ZBBlockDescriptionFlagsHasStret = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
24     ZBBlockDescriptionFlagsHasSignature = (1 << 30)
25 };
26 typedef int ZBBlockDescriptionFlags;

block其实就是个struct类型,其中的descriptor中的signature正是我们想要的block的方法签名。那么我们只需要获取到signature,然后转换成NSMethodSignature,即可轻易的获取到我们想要的block的参数和返回值类型,并且通过NSInvocation执行block。

以下就是如何获取signature的方法:

 1 + (NSMethodSignature *)getSignatureWithBlock:(id)block{
 2     struct ZBBlockLiteral *blockRef = (__bridge struct ZBBlockLiteral *)block;
 3     ZBBlockDescriptionFlags _flags = blockRef->flags;
 4     if (_flags & ZBBlockDescriptionFlagsHasSignature) {
 5         void *signatureLocation = blockRef->descriptor;
 6         signatureLocation += sizeof(unsigned long int); 
 7         signatureLocation += sizeof(unsigned long int);
 8         
 9         if (_flags & ZBBlockDescriptionFlagsHasCopyDispose) {
10             signatureLocation += sizeof(void(*)(void *dst, void *src));
11             signatureLocation += sizeof(void (*)(void *src));
12         }
13         
14         const char *signature = (*(const char **)signatureLocation);
15         return [NSMethodSignature signatureWithObjCTypes:signature];
16     }
17     return nil;
18 }

其中 signatureLocation += sizeof(unsigned long int);的加号,就是c/c++中的指针移动,通过指针移动,我们就可以找到signature的指针。然后通过[NSMethodSignature signatureWithObjCTypes:signature]构建方法签名的objc对象。

下面是测试代码:

 1 BOOL(^testBlock)(NSString *, NSString *) = ^(NSString *str1, NSString *str2) {
 2         NSLog(@"-----:%@-------:%@", str1, str2);
 3         return  [str1 isEqualToString:str2];
 4     };
 5     
 6     NSMethodSignature *sign = [MyBlockDesc getSignatureWithBlock:testBlock];
 7     
 8     if (sign) {
 9         
10         NSLog(@"----参数个数:%@", @(sign.numberOfArguments));
11         NSLog(@"----返回值类型:%@", [NSString stringWithUTF8String:sign.methodReturnType]);
12         for (int i=0; i<sign.numberOfArguments; i++) {
13             NSLog(@"----第%@个参数:%@", @(i), [NSString stringWithUTF8String:[sign getArgumentTypeAtIndex:i]]);
14         }
15         NSString *str1 = @"111";
16         NSString *str2 = @"111";
17         NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sign];
18         [inv setArgument:&str1 atIndex:1];
19         [inv setArgument:&str2 atIndex:2];
20         inv.target = testBlock;
21         [inv invoke];
22         BOOL res;
23         [inv getReturnValue:&res];
24         
25         NSLog(@"----返回结果:%@", @(res));
26         
27     }else {
28         NSLog(@"------参数签名为空");
29     }

打印日志如下:

1 2020-01-09 09:32:04.600723+0800 Test[2503:27368] ----参数个数:3
2 2020-01-09 09:32:04.600908+0800 Test[2503:27368] ----返回值类型:B
3 2020-01-09 09:32:04.601046+0800 Test[2503:27368] ----第0个参数:@?
4 2020-01-09 09:32:04.601166+0800 Test[2503:27368] ----第1个参数:@"NSString"
5 2020-01-09 09:32:04.601277+0800 Test[2503:27368] ----第2个参数:@"NSString"
6 2020-01-09 09:32:04.601380+0800 Test[2503:27368] -----:111-------:111
7 2020-01-09 09:32:04.601493+0800 Test[2503:27368] ----返回结果:1

其中第0个参数就是block对象自身。因此我们既可以通过如上手段,来扩展实现一个线程安全的block调用,结合可变参数的使用,就可以实现任意参数的block的调用。

posted @ 2020-01-09 09:36  zbblogs  阅读(1441)  评论(0编辑  收藏  举报