NSInvocation如何調(diào)用block
同步發(fā)布到博客地址NSInvocation如何調(diào)用block
NSInvocation是調(diào)用函數(shù)的另一種方式杂瘸,它將調(diào)用者队腐,函數(shù)名,參數(shù)封裝到一個(gè)對象氓皱,然后通過一個(gè)invoke函數(shù)來執(zhí)行被調(diào)用的函數(shù)河胎,其思想就是命令者模式,將請求封裝成對象幔嗦。
NSMethodSignature 用于描述 method 的類型信息:返回值類型酿愧,及每個(gè)參數(shù)的類型。?
NSInvocation 簡單實(shí)用
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];
在 xxx
的 isa
指向的地方的方法列表里面找方法邀泉;
[xxx instanceMethodSignatureForSelector:@select];
直接在 xxx
的 方法列表里面找方法嬉挡。
signatureWithObjCTypes
根據(jù)字符創(chuàng)建簽名叛氨。
具體規(guī)則可以查看蘋果的官方文檔:Type Encodings
[invocation setArgument:&argument1 atIndex:2];
這里設(shè)置參數(shù)要從 2 開始,是因?yàn)槟J(rèn)方法有兩個(gè)參數(shù) 0: self
棘伴,1: SEL
寞埠。
還有一個(gè)點(diǎn)要注意,經(jīng)常當(dāng)我們通過 getReturnValue
獲取返回值的時(shí)候焊夸,容易出現(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
這是因?yàn)?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
如何獲取一個(gè) block 的方法簽名
這一段來自于 用 Block 實(shí)現(xiàn)委托方法
通過種種渠道我們可以得知 block揪阶,最終的結(jié)果是一個(gè)結(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
這個(gè)位置。
獲取的方法:
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
侨艾, 第一個(gè)參數(shù)是返回類型,第二個(gè)參數(shù)才是真正的參數(shù)拓挥,并不像 select
第二個(gè)參數(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];
}
}