獲取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的調用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM