一、NSInvocation简介与作用
NSInvocation是Objective-C中的一个类,它用于封装一个方法调用时所需要的所有信息,包括方法名、参数、返回值等。通过NSInvocation对象,我们可以灵活地调用任何方法,甚至可以在运行时得到任何方法的参数和返回值。
NSInvocation通常用于实现一些高级的功能,比如消息转发、RPC(远程过程调用)等。在iOS开发中,我们通常用NSInvocation实现一些特定的需求,比如多线程回调、方法动态代理等。
以下是NSInvocation的定义:
@interface NSInvocation : NSObject @property (nullable, retain) NSMethodSignature *methodSignature; @property (readonly, retain) id target; @property SEL selector; - (void)invoke; // ... @end
NSInvocation的方法比较多,但是最重要的几个属性是:methodSignature、target和selector。methodSignature用于描述方法的参数及返回值类型,target是方法所在的对象,selector是方法名。
二、创建NSInvocation对象
我们可以通过以下两种方式来创建一个NSInvocation对象:
1、使用NSInvocation的类方法createInvocationWithMethodSignature:
使用这种方式创建NSInvocation对象,我们需要先获得方法的NSMethodSignature对象,然后通过它来创建NSInvocation对象。
SEL selector = @selector(foo:bar:); NSMethodSignature *methodSignature = [MyClass instanceMethodSignatureForSelector:selector]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
这样,我们就创建了一个用于调用-(void)foo:(id)obj1 bar:(id)obj2;方法的NSInvocation对象。
2、使用NSInvocation的init方法:
使用这种方式创建NSInvocation对象,我们需要先给target和selector属性赋值,然后调用init方法创建NSInvocation对象:
SEL selector = @selector(foo:bar:); id target = [MyClass new]; NSInvocation *invocation = [[NSInvocation alloc] init]; invocation.target = target; invocation.selector = selector;
这样,我们也创建了一个用于调用-(void)foo:(id)obj1 bar:(id)obj2;方法的NSInvocation对象。
三、设置NSInvocation对象的参数
通过NSInvocation对象,我们可以设置方法调用时的参数,这些参数必须按照NSMethodSignature对象描述的参数类型与顺序指定。
举个例子,假设我们要调用以下方法:
- (void)foo:(id)obj1 bar:(id)obj2;
我们就需要依次设置两个参数:
id obj1 = ...; id obj2 = ...; [invocation setArgument:&obj1 atIndex:2]; [invocation setArgument:&obj2 atIndex:3];
在上面的代码中,我们使用了setArgument:atIndex:方法来设置NSInvocation对象的参数值。第二个参数是参数的下标,注意参数下标从2开始,因为0位置是self,1位置是_cmd。
NSInvocation支持多种参数类型,比如BOOL、int、float、double、struct等,不同的参数类型对应不同的setArgument:atIndex:方法。
四、调用NSInvocation对象
NSInvocation对象创建好后,我们可以通过调用NSInvocation的invoke方法来执行方法调用:
[invocation invoke];
NSInvocation对象的invoke方法会调用我们所设置的方法,并传入所设置的参数。在方法调用完成后,我们就可以通过NSInvocation来获取方法的返回值和异常信息等。
五、获取方法的返回值
NSInvocation除了可以设置参数之外,还可以获取方法的返回值。
如果方法返回值是基本类型(如int、float),我们可以直接通过NSInvocation的getReturnValue:方法来获取返回值。如果方法返回的是一个对象,我们需要先使用NSInvocation的methodSignature属性来获取返回值类型,然后使用getReturnValue:方法来获取返回值。
//方法返回值是float类型 float result = 0.0f; [invocation getReturnValue:&result]; //方法返回值是对象 NSMethodSignature *methodSignature = invocation.methodSignature; id result = nil; if (strcmp(methodSignature.methodReturnType, "@") == 0) { [invocation getReturnValue:&result]; }
六、示例代码
下面是使用NSInvocation实现回调的示例代码:
@interface MyClass () @property (strong, nonatomic) NSString *name; @end @implementation MyClass - (void)updateName:(NSString *)name { self.name = name; } - (void)doSomethingWithCompletion:(void (^)(void))completion { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //模拟耗时操作 [NSThread sleepForTimeInterval:3.0]; //执行回调 if (completion) { NSMethodSignature *methodSignature = [self methodSignatureForSelector:@selector(updateName:)]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; invocation.target = self; invocation.selector = @selector(updateName:); NSString *name = @"Peter"; [invocation setArgument:&name atIndex:2]; completion(invocation); } }); } @end //在其他地方调用 MyClass *obj = [MyClass new]; [obj doSomethingWithCompletion:^(NSInvocation *invocation) { dispatch_async(dispatch_get_main_queue(), ^{ [invocation invoke]; NSLog(@"%@", obj.name); }); }];
在上面的代码中,我们通过NSInvocation来实现了一个异步回调的操作。在doSomethingWithCompletion:方法中,我们使用NSMethodSignature和NSInvocation来封装一个updateName:方法调用,并通过completion将这个NSInvocation对象传递给外部。在外部回调方法中,我们先调用NSInvocation的invoke方法来执行updateName:方法,然后通过obj.name获取更新后的值。
原创文章,作者:NOQO,如若转载,请注明出处:https://www.506064.com/n/130973.html