美文网首页
NSInvocation的使用

NSInvocation的使用

作者: 想聽丿伱說衹愛我 | 来源:发表于2020-08-21 15:46 被阅读0次

    版本: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的使用

    相关文章

      网友评论

          本文标题:NSInvocation的使用

          本文链接:https://www.haomeiwen.com/subject/nslujktx.html