NSInvocation的使用

一、介绍

在 iOS中可以直接调用方法的方式有两种:

1、performSelector:withObject;
2、NSInvocation

前者最多只能有两个参数,多参的情况下需要用NSInvocation来处理。

NSInvocation.h文件:

@interface NSInvocation : NSObject {
@private
    void *_frame;
    void *_retdata;
    id _signature;
    id _container;
    uint8_t _retainedArgs;
    uint8_t _reserved[15];
}
// 通过NSMethodSignature对象创建NSInvocation对象(NSMethodSignature为方法签名类)
+ (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)sig;

@property (readonly, retain) NSMethodSignature *methodSignature;

// 保留参数,将传入的所有参数以及target都retain一遍
- (void)retainArguments;

// 调用retainArguments之前,值为NO,调用之后值为YES
@property (readonly) BOOL argumentsRetained;

// 消息调用者
@property (nullable, assign) id target;

// 调用的消息
@property SEL selector;

// 获取消息返回值
- (void)getReturnValue:(void *)retLoc;
// 设置消息返回值
- (void)setReturnValue:(void *)retLoc;

// 获取消息参数
- (void)getArgument:(void *)argumentLocation atIndex:(NSInteger)idx;
// 设置消息参数
- (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx;

// 发送消息(执行)
- (void)invoke;
// target发送消息
- (void)invokeWithTarget:(id)target;

@end

上面介绍到创建一个NSInvocation对象需要NSMethodSignature对象(方法签名类),所以我们再了解下NSMethodSignature类

@interface NSMethodSignature : NSObject {
@private
    void *_private;
    void *_reserved[5];
    unsigned long _flags;
}

// 通过Objective-C类型编码(Objective-C Type Encodings)创建一个NSMethodSignature对象
+ (nullable NSMethodSignature *)signatureWithObjCTypes:(const char *)types;

// 参数个数
@property (readonly) NSUInteger numberOfArguments;
// 获取参数类型
- (const char *)getArgumentTypeAtIndex:(NSUInteger)idx NS_RETURNS_INNER_POINTER;

// 所有参数占用的字节数
@property (readonly) NSUInteger frameLength;

// 通过分布式对象调用时,接收者是否是异步的。
- (BOOL)isOneway;

// 方法返回类型
@property (readonly) const char *methodReturnType NS_RETURNS_INNER_POINTER;
// 返回值所需的字节数。
@property (readonly) NSUInteger methodReturnLength;

@end

二、使用

1、无参无返

- (void)invocation{
    SEL sel = @selector(run);
    NSMethodSignature *sign = [[self class] instanceMethodSignatureForSelector:sel];
    NSInvocation *invo = [NSInvocation invocationWithMethodSignature:sign];
    invo.target = self;
    invo.selector = sel;
    [invo invoke];
}

- (void)run{
    NSLog(@"run");
}

2、有参无返

- (void)invocation{
    SEL sel = @selector(runArg1:arg2:arg3:);
    NSMethodSignature *sign = [[self class] instanceMethodSignatureForSelector:sel];
    NSInvocation *invo = [NSInvocation invocationWithMethodSignature:sign];
    invo.target = self;
    invo.selector = sel;
    // 参数设置
    NSString *name = @"小明";
    NSInteger age = 18;
    NSArray *family = @[@"爷爷",@"奶奶",@"爸爸",@"妈妈"];
    // 0是target 1是selector
    [invo setArgument:&name atIndex:2];
    [invo setArgument:&age atIndex:3];
    [invo setArgument:&family atIndex:4];
    
    [invo invoke];
}

- (void)runArg1:(NSString *)arg1 arg2:(NSInteger)arg2 arg3:(NSArray *)arg3{
    NSLog(@"%@ %ld %@",arg1,(long)arg2,arg3);
}

3、有参有返

- (void)invocation{
    SEL sel = @selector(runArg1:arg2:arg3:);
    NSMethodSignature *sign = [[self class] instanceMethodSignatureForSelector:sel];
    NSInvocation *invo = [NSInvocation invocationWithMethodSignature:sign];
    invo.target = self;
    invo.selector = sel;
    // 参数设置
    NSString *name = @"小明";
    NSInteger age = 18;
    NSArray *family = @[@"爸爸",@"妈妈"];
    // 0是target 1是selector
    [invo setArgument:&name atIndex:2];
    [invo setArgument:&age atIndex:3];
    [invo setArgument:&family atIndex:4];
    
    [invo invoke];
    
    // 获取返回值
    void *re;
    [invo getReturnValue:&re];
    NSLog(@"%@",re);
}

- (NSString *)runArg1:(NSString *)arg1 arg2:(NSInteger)arg2 arg3:(NSArray *)arg3{
    NSLog(@"%@ %ld %@",arg1,(long)arg2,arg3);
    return [NSString stringWithFormat:@"%@今年%ld岁,家庭成员有%@,%@",arg1,(long)arg2,arg3[0],arg3[1]];
}

注:这里要说明一点,若用NSString 类型去获取返回值会crash。具体看这篇

参考:https://blog.csdn.net/ssirreplaceable/article/details/53375972

你可能感兴趣的:(NSInvocation的使用)