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