NSInvocation如何調(diào)用block

NSInvocation如何調(diào)用block

同步發(fā)布到博客地址NSInvocation如何調(diào)用block

NSInvocation是調(diào)用函數(shù)的另一種方式,它將調(diào)用者,函數(shù)名,參數(shù)封裝到一個對象,然后通過一個invoke函數(shù)來執(zhí)行被調(diào)用的函數(shù),其思想就是命令者模式,將請求封裝成對象。
NSMethodSignature 用于描述 method 的類型信息:返回值類型,及每個參數(shù)的類型。?

NSInvocation 簡單實用

NSMethodSignature *signature = [NSInvocationTestViewController instanceMethodSignatureForSelector:@selector(testInstanceMethodArgument1:)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = @selector(testInstanceMethodArgument1:);
NSString *argument1 = @"string";
[invocation setArgument:&argument1 atIndex:2];
[invocation invoke];

獲取方法簽名的方法

  • instanceMethodSignatureForSelector
  • methodSignatureForSelector
  • signatureWithObjCTypes

[xxx methodSignatureForSelector:@select];

xxxisa 指向的地方的方法列表里面找方法;

[xxx instanceMethodSignatureForSelector:@select];

直接在 xxx 的 方法列表里面找方法。

signatureWithObjCTypes

根據(jù)字符創(chuàng)建簽名。

具體規(guī)則可以查看蘋果的官方文檔:Type Encodings

[invocation setArgument:&argument1 atIndex:2];

這里設(shè)置參數(shù)要從 2 開始,是因為默認(rèn)方法有兩個參數(shù) 0: self ,1: SEL

還有一個點要注意,經(jīng)常當(dāng)我們通過 getReturnValue 獲取返回值的時候,容易出現(xiàn)崩潰。

譬如 返回值是 NSString *

 NSString *returnValue;
 NSLog(@"1 returnValue == %@",returnValue);
 [invocation getReturnValue:&returnValue];
 NSLog(@"2 returnValue == %@",returnValue);
 NSLog(@"returnValue %p",returnValue);
 
 ---
 *** -[CFString release]: message sent to deallocated instance 0x60000025db30

這是因為 getReturnValue 接收的參數(shù)為 void * 類型,在 ARC 模式下,強(qiáng)轉(zhuǎn)類型導(dǎo)致了內(nèi)存管理的混亂。下面這張寫法就不會有問題。

void *returnValue = NULL;
NSLog(@"1 returnValue == %@",returnValue);
[invocation getReturnValue:&returnValue];
NSLog(@"2 returnValue == %@",returnValue);
NSString *t = (__bridge NSString *)(returnValue);
NSLog(@"t %p",t);

NSInvocation 如何調(diào)用 block

如何獲取一個 block 的方法簽名

這一段來自于 用 Block 實現(xiàn)委托方法

通過種種渠道我們可以得知 block,最終的結(jié)果是一個結(jié)構(gòu)體。形式如下

// Block internals.
typedef NS_OPTIONS(int, TBVBlockFlags) {
    TBVBlockFlagsHasCopyDisposeHelpers = (1 << 25),
    TBVBlockFlagsHasSignature          = (1 << 30)
};
typedef struct tbv_block {
    __unused Class isa;
    TBVBlockFlags flags;
    __unused int reserved;
    void (__unused *invoke)(struct tbv_block *block, ...);
    struct {
        unsigned long int reserved;
        unsigned long int size;
        // requires TBVBlockFlagsHasCopyDisposeHelpers
        void (*copy)(void *dst, const void *src);
        void (*dispose)(const void *);
        // requires TBVBlockFlagsHasSignature
        const char *signature;
        const char *layout;
    } *descriptor;
    // imported variables
} *TBVBlockRef;

方法的簽名就位于 TBVBlockRef -> descriptor -> signature 這個位置。
獲取的方法:

static NSMethodSignature *tbv_signatureForBlock(id block) {
    TBVBlockRef layout = (__bridge TBVBlockRef)(block);
    
    // 沒有簽名,直接返回空
    if (!(layout->flags & TBVBlockFlagsHasSignature)) {
        return nil;
    }
    
    // 獲取 descriptor 指針
    void *desc = layout->descriptor;
    
    // 跳過 reserved 和 size 成員
    desc += 2 * sizeof(unsigned long int);
    
    // 如果有 Helpers 函數(shù), 跳過 copy 和 dispose 成員
    if (layout->flags & TBVBlockFlagsHasCopyDisposeHelpers) {
        desc += 2 * sizeof(void *);
    }
    
    // desc 為 signature 指針的地址,轉(zhuǎn)換下給 objcTypes
    char *objcTypes = (*(char **)desc);
    
    return [NSMethodSignature signatureWithObjCTypes:objcTypes];
}

對比了發(fā)現(xiàn)和 Aspects 里面獲取 block 簽名的方法一致。

如何為 block 設(shè)置參數(shù)

block 的簽名不像 select, 第一個參數(shù)是返回類型,第二個參數(shù)才是真正的參數(shù),并不像 select 第二個參數(shù)是 : 代表 SEL.

   for (int idx = 1; idx < methodSignature.numberOfArguments; idx++) {
        
        // 獲取參數(shù)類型
        const char *type = [methodSignature getArgumentTypeAtIndex:idx];
        NSLog(@"----%s",type);
        if ([[NSString stringWithUTF8String:type] isEqualToString:@"\@\"NSString\""] ) {
            NSString *argument1 = @"----123---";
            [blockInvocation setArgument:&argument1 atIndex:idx];
        }else if ([[NSString stringWithUTF8String:type] isEqualToString:@"#"] )
        {
            Class cls = [NSSet class];
            [blockInvocation setArgument:&cls atIndex:idx];
        }
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容