NSInvocation的使用

版本:iOS13.6

一、简介

通常调用方法的方式是使用[实例 方法名][实例 方法名:参数]

[self methodName];
或
[self methodName:array];

若该方法没有公开,可以使用NSObject的performSelector方法,但performSelector只支持调用最多两个入参且入参类型和返回类型为id的方法。

id returnValue = [self performSelector:@selector(methodName)];
或
id returnValue = [self performSelector:@selector(methodName) withObject:@"object1"];
或
id returnValue = [self performSelector:@selector(methodName) withObject:@"object1" withObject:@"object2"];

若入参的个数多于两个,可以使用NSInvocation来调用方法。

二、NSInvocation的API

@interface NSInvocation : NSObject

//根据方法签名来初始化实例对象
//方法签名 可查看第三节
+ (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)sig;
//对象的方法签名 只读
@property (readonly, retain) NSMethodSignature *methodSignature;
//强引用传入的参数,防止参数被释放
- (void)retainArguments;
//当前的参数是否为强引用 只读
@property (readonly) BOOL argumentsRetained;
//调用该方法的对象
@property (nullable, assign) id target;
//要调用的方法的选择器 
@property SEL selector;

//获取该方法的返回值
//retLoc 一个变量的地址,该变量会保存返回值
- (void)getReturnValue:(void *)retLoc;
//设置该方法的返回值,虽然方法会调用,但返回值则会被该值替换
//retLoc 一个变量的地址,该变量的值即为要设置的返回值
- (void)setReturnValue:(void *)retLoc;

//获取该方法对应索引的参数值
//argumentLocation 一个变量的地址,该变量会保存参数的值
//idx 第几个参数 从2开始 前两个分别被该方法的self与_cmd占用
- (void)getArgument:(void *)argumentLocation atIndex:(NSInteger)idx;
//设置该方法对应索引的参数值
//argumentLocation 一个变量的地址,该变量的值即为要设置的参数值
//idx 第几个参数 从2开始 前两个分别被该方法的self与_cmd占用
- (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx;

//调用
- (void)invoke;
//调用 会替换属性target
- (void)invokeWithTarget:(id)target;

@end

三、NSMethodSignature的API

可通过NSObject的实例方法methodSignatureForSelector和类方法instanceMethodSignatureForSelector来创建方法签名。

NSMethodSignature *signature = [self methodSignatureForSelector:NSSelectorFromString(@"methodName")];
或
NSMethodSignature *signature = [self.class instanceMethodSignatureForSelector:NSSelectorFromString(@"")];
@interface NSMethodSignature : NSObject

//通过方法的类型字符串初始化实例
//类型字符串 可查看第四节
+ (nullable NSMethodSignature *)signatureWithObjCTypes:(const char *)types;
//该方法的参数数量,包括方法自带的self和_cmd
@property (readonly) NSUInteger numberOfArguments;
//获取对应索引的参数类型字符串
- (const char *)getArgumentTypeAtIndex:(NSUInteger)idx;
//该方法所占的字节数
@property (readonly) NSUInteger frameLength;
//是否为单向(不知是何意)
- (BOOL)isOneway;

//该方法的返回类型字符串
@property (readonly) const char *methodReturnType;
//该方法的返回类型值所占的字节数
@property (readonly) NSUInteger methodReturnLength;

@end

四、类型字符串

可通过@encode(type)来获取类型字符串
例如:
@encode(NSString)的类型字符串为@
@encode(NSInteger)的类型字符串为q
@encode(double)的类型字符串为d
具体的可查看下面的枚举,虽然被废弃了,但大体没有变化。

enum _NSObjCValueType {
    NSObjCNoType = 0,
    NSObjCVoidType = 'v',
    NSObjCCharType = 'c',
    NSObjCShortType = 's',
    NSObjCLongType = 'l',
    NSObjCLonglongType = 'q',
    NSObjCFloatType = 'f',
    NSObjCDoubleType = 'd',
    NSObjCBoolType = 'B',
    NSObjCSelectorType = ':',
    NSObjCObjectType = '@',
    NSObjCStructType = '{',
    NSObjCPointerType = '^',
    NSObjCStringType = '*',
    NSObjCArrayType = '[',
    NSObjCUnionType = '(',
    NSObjCBitfield = 'b'
}API_DEPRECATED

上面是单个变量的类型字符串,但NSMethodSignature的初始化方法signatureWithObjCTypes需要传入整个方法的类型字符串,具体是怎样的呢?

有一个方法

- (NSString *)getAdressByName:(NSString *)name byAge:(NSInteger)age {
    NSLog(@"name = %@ age = %ld", name, age);
    return @"cd";
}

可使用methodSignatureForSelector来获取该方法的签名

NSMethodSignature *signature = [self methodSignatureForSelector:@selector(getAdressByName:byAge:)];

断点后,可看到有一个参数_typeString值为@32@0:8@16q24

image.png

@32表示方法的返回类型NSString *
@0和:8表示方法自带的参数self_cmd
@16表示自己设置的参数(NSString *)name
q24表示自己设置的参数(NSInteger)age
所以使用signatureWithObjCTypes初始化实例,可以照下面所示来初始化。

NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"@32@0:8@16q24"];
也可以
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"@@:@q"];

    SEL sel = @selector(getAdressByName:byAge:);
    //通过NSObject的实例方法来获取方法签名
    NSMethodSignature *signature = [self methodSignatureForSelector:sel];
    //通过NSObject的类方法来获取方法签名
//    NSMethodSignature *signature = [self.class instanceMethodSignatureForSelector:sel];
    //通过方法类型字符串来获取方法签名
//    NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"@32@0:8@16q24"];
    //通过方法类型字符串来获取方法签名
//    NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"@@:@q"];
    
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    //防止参数被释放
    [invocation retainArguments];
    //方法调用的对象是self
    invocation.target = self;
    //方法的选择器
    invocation.selector = sel;
    //设置第2个参数的值 第0、1参数被方法的self与_cmd占用
    NSString *name = @"zhangsan";
    [invocation setArgument:&name atIndex:2];
    //设置第3个参数的值
    NSInteger age = 20;
    [invocation setArgument:&age atIndex:3];
    [invocation invoke];
    
    //获取方法的返回值 该方法需要在invoke之后调用,否则是nil
    NSString *returnVlaue;
    [invocation getReturnValue:&returnVlaue];
    //获取第2个参数值
    NSString *argument2;
    [invocation getArgument:&argument2 atIndex:2];
    //获取第3个参数值
    NSInteger argument3;
    [invocation getArgument:&argument3 atIndex:3];
    NSLog(@"%@ %@ %ld", returnVlaue, argument2, argument3);

- (NSString *)getAdressByName:(NSString *)name byAge:(NSInteger)age {
    NSLog(@"name = %@ age = %ld", name, age);
    return @"cd";
}
输出:
name = zhangsan age = 20
cd zhangsan 20

若将下面代码放入invoke前面,可使该方法的返回值变为 成都

    //设置返回值
    NSString *setReturnVlaue = @"成都";
    [invocation setReturnValue:&setReturnVlaue];

本文参考iOS - NSInvocation的使用

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