获取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的调用。
要得到你必须要付出,要付出你还要学会坚持,如果你真的觉得很难,那你就放弃,但是你放弃了就不要抱怨,我觉得人生就是这样,世界真的是平等的,每个人都要通过自己的努力,去决定自己生活的样子。