首先我们要了解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的调用。
原文链接: https://www.cnblogs.com/zbblog/p/12169686.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/191709
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!