iOS开发 - performSelector:传递两个以上参数传、递结构体

▶ 如何传递两个以上的参数

下面用三种方式实现

DemoA:将所有参数放入一个字典或数组传过去!这种方式需要改动要调用方法的取参数的方式,比如使用字典传值时,双方需约定每一个参数放入字典对应的 key 是什么

- (void)viewDidLoad {
    [super viewDidLoad];

    NSDictionary *paramDic = @{@"param1":@"string",@"param2":@[@2,@3,@3],@"param3":@123};
    [self performSelector:@selector(testFunctionWithParams:) withObject:paramDic];
}

- (void)testFunctionWithParams:(NSDictionary *)paramDic {
    NSLog(@"%s dic:%@",__FUNCTION__, paramDic);
}

DemoB:使用 objc_msgSend() 。利用这种方法可以传递多个参数的特性调用方法执行

- (void)viewDidLoad {
    [super viewDidLoad];
    // 需要引入头文件  #import <objc/message.h>
    ((void (*)(id,SEL,NSString *, NSArray *, NSInteger))objc_msgSend)(self, @selector(textFunctionWithParam:param2:param3:),@"111",@[@2,@3],123);
}

-(void)textFunctionWithParam:(NSString *)param1 param2:(NSArray *)param2 param3:(NSInteger)param3 {
    NSLog(@"param1:%@, param2:%@, param3:%ld",param1, param2, param3);
}

DemoC:使用 NSInvocation

 1 #import "ViewController.h"
 2 @implementation ViewController
 3 
 4 - (void)viewDidLoad {
 5     [super viewDidLoad];
 6     
 7     NSArray *paramArray = [NSArray arrayWithObjects:@"hello", @[@222,@333],@122,nil];
 8     [self performSelector:@selector(textFunctionWithParam:param2:param3:) withObjects:paramArray];
 9 }
10 
11 - (id)performSelector:(SEL)selector withObjects:(NSArray *)objects{
12     
13     // 方法签名
14     NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
15     
16     if (signature == nil) {
17         // 可以抛出异常也可以不操作
18     }
19     
20     // 通过 NSInvocation对象 包装一次方法调用:方法调用者、方法名、方法参数、方法返回值
21     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];// 可以使用方法的签名来创建一个 NSInvocation对象
22     invocation.target = self;
23     invocation.selector = selector;
24     
25     // NSMethodSignature 有两个常用的只读属性
26     // numberOfArguments   方法参数的个数
27     // methodReturnLength  方法返回值类型的长度,大于 0 表示有返回值
28     NSInteger paramsCount = signature.numberOfArguments - 2; // 除self、_cmd 以外的参数个数
29     paramsCount = MIN(paramsCount, objects.count);
30     
31     // 参数
32     for (NSInteger i = 0; i < paramsCount; i++) {
33         id object = objects[i];
34         if ([object isKindOfClass:[NSNull class]]) continue;
35         
36         // 设置参数时必须传递参数的地址
37         // 参数赋值:注意下标从 2 开始,因为 0、1 已经被 target 与 selector 占用
38         [invocation setArgument:&object atIndex:i + 2];
39     }
40     
41     // 调用方法:有返回值类型,才去获得返回值
42     [invocation invoke];
43     
44     // 获取返回值
45     id returnValue = nil;
46     if (signature.methodReturnLength) {
47         // 返回值与设置参数一样,都必须将地址传过去
48         [invocation getReturnValue:&returnValue];
49     }
50     return returnValue;
51 }
52 
53 // 调用的方法
54 -(void)textFunctionWithParam:(NSString *)param1 param2:(NSArray *)param2 param3:(NSString*)param3 {
55     
56     NSLog(@"param1:%@, param2:%@, param3:%d",param1, param2, [param3 intValue]);
57 }
58 
59 @end

▶ 传递结构体

我们知道 NSNumber 是 NSValue 的子类,但 NSNumber 只能包装数字类型,而 NSValue 可以包装任意值

因此可以用 NSValue 将结构体包装后加入 NSArray或NSDictionary 中,代码如下

#import "ViewController.h"
typedef struct MakeStruct{
    int x;
    int y;
}testStruct;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 结构体
    testStruct testS = {101, 192};
    NSValue *valueStruct = [NSValue valueWithBytes:&testS objCType:@encode(testStruct)];
    
    NSDictionary *dataDic = @{@"param1":@"ssfs",@"param2":@[@333,@32343],@"param3":valueStruct};
    [self performSelector:@selector(testFunctionWithParams:) withObject:dataDic];
}

- (void)testFunctionWithParams:(NSDictionary *)paramDic {
    NSValue *paramValue = paramDic[@"param3"];
    testStruct paramStruct;
    [paramValue getValue:&paramStruct];
    NSLog(@"取出结构体中的 x 值:%d",paramStruct.x);
}

@end

 

posted on 2022-03-22 10:47  低头捡石頭  阅读(399)  评论(0)    收藏  举报

导航