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/zh-tw/n/130973.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
NOQO的頭像NOQO
上一篇 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

發表回復

登錄後才能評論