NSInvocation如何调用block

NSInvocation如何调用block

同步发布到博客地址NSInvocation如何调用block

NSInvocation是调用函数的另一种方式,它将调用者,函数名,参数封装到一个对象,然后通过一个invoke函数来执行被调用的函数,其思想就是命令者模式,将请求封装成对象。
NSMethodSignature 用于描述 method 的类型信息:返回值类型,及每个参数的类型。�

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

根据字符创建签名。

具体规则可以查看苹果的官方文档:Type Encodings

[invocation setArgument:&argument1 atIndex:2];

这里设置参数要从 2 开始,是因为默认方法有两个参数 0: self ,1: SEL

还有一个点要注意,经常当我们通过 getReturnValue 获取返回值的时候,容易出现崩溃。

譬如 返回值是 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 接收的参数为 void * 类型,在 ARC 模式下,强转类型导致了内存管理的混乱。下面这张写法就不会有问题。

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

NSInvocation 如何调用 block

如何获取一个 block 的方法签名

这一段来自于 用 Block 实现委托方法

通过种种渠道我们可以得知 block,最终的结果是一个结构体。形式如下

// 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 函数, 跳过 copy 和 dispose 成员
    if (layout->flags & TBVBlockFlagsHasCopyDisposeHelpers) {
        desc += 2 * sizeof(void *);
    }
    
    // desc 为 signature 指针的地址,转换下给 objcTypes
    char *objcTypes = (*(char **)desc);
    
    return [NSMethodSignature signatureWithObjCTypes:objcTypes];
}

对比了发现和 Aspects 里面获取 block 签名的方法一致。

如何为 block 设置参数

block 的签名不像 select, 第一个参数是返回类型,第二个参数才是真正的参数,并不像 select 第二个参数是 : 代表 SEL.

   for (int idx = 1; idx < methodSignature.numberOfArguments; idx++) {
        
        // 获取参数类型
        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];
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容