NSInvocation如何实现方法调用和参数传递

一、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

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
NOQONOQO
上一篇 2024-10-03 23:42
下一篇 2024-10-03 23:42

相关推荐

  • 解决.net 6.0运行闪退的方法

    如果你正在使用.net 6.0开发应用程序,可能会遇到程序闪退的情况。这篇文章将从多个方面为你解决这个问题。 一、代码问题 代码问题是导致.net 6.0程序闪退的主要原因之一。首…

    编程 2025-04-29
  • ArcGIS更改标注位置为中心的方法

    本篇文章将从多个方面详细阐述如何在ArcGIS中更改标注位置为中心。让我们一步步来看。 一、禁止标注智能调整 在ArcMap中设置标注智能调整可以自动将标注位置调整到最佳显示位置。…

    编程 2025-04-29
  • Python创建分配内存的方法

    在python中,我们常常需要创建并分配内存来存储数据。不同的类型和数据结构可能需要不同的方法来分配内存。本文将从多个方面介绍Python创建分配内存的方法,包括列表、元组、字典、…

    编程 2025-04-29
  • Python中init方法的作用及使用方法

    Python中的init方法是一个类的构造函数,在创建对象时被调用。在本篇文章中,我们将从多个方面详细讨论init方法的作用,使用方法以及注意点。 一、定义init方法 在Pyth…

    编程 2025-04-29
  • 用不同的方法求素数

    素数是指只能被1和自身整除的正整数,如2、3、5、7、11、13等。素数在密码学、计算机科学、数学、物理等领域都有着广泛的应用。本文将介绍几种常见的求素数的方法,包括暴力枚举法、埃…

    编程 2025-04-29
  • 使用Vue实现前端AES加密并输出为十六进制的方法

    在前端开发中,数据传输的安全性问题十分重要,其中一种保护数据安全的方式是加密。本文将会介绍如何使用Vue框架实现前端AES加密并将加密结果输出为十六进制。 一、AES加密介绍 AE…

    编程 2025-04-29
  • 三星内存条参数用法介绍

    本文将详细解释三星内存条上面的各种参数,让你更好地了解内存条并选择适合自己的一款。 一、容量大小 容量大小是内存条最基本的参数,一般以GB为单位表示,常见的有2GB、4GB、8GB…

    编程 2025-04-29
  • Python中读入csv文件数据的方法用法介绍

    csv是一种常见的数据格式,通常用于存储小型数据集。Python作为一种广泛流行的编程语言,内置了许多操作csv文件的库。本文将从多个方面详细介绍Python读入csv文件的方法。…

    编程 2025-04-29
  • Python3定义函数参数类型

    Python是一门动态类型语言,不需要在定义变量时显示的指定变量类型,但是Python3中提供了函数参数类型的声明功能,在函数定义时明确定义参数类型。在函数的形参后面加上冒号(:)…

    编程 2025-04-29
  • Python学习笔记:去除字符串最后一个字符的方法

    本文将从多个方面详细阐述如何通过Python去除字符串最后一个字符,包括使用切片、pop()、删除、替换等方法来实现。 一、字符串切片 在Python中,可以通过字符串切片的方式来…

    编程 2025-04-29

发表回复

登录后才能评论