用NSInvocation來作為object performSelector的替代方案

在寫程式的時候, 常常會有需要呼叫好幾個method, 然後在時間上有所需別; 最常遇到的都是希望Layout改變完成之後再去執行下一步動作, 如果是一堆layout animation, 就會很常使用到delay去做一些時間差的區別.

can do it this way
1
2
3
4
5
6
7
- (void)currentMehtod {  SEL action2 = @selector(action2:);  [object performSelector:action2 withObject:data afterDelay:1.f];  /** …  * do somethig after …  **/ } 

一開始使用的時候覺得還好, 但是慢慢的發現如果我的method一開始寫的時候帶入多個參數, 如果用上面的方式, 就要改寫本來的method, 這樣又得花時間, 而且可能最後都是帶入NSDictionary的物件, 在把需要的資料拉出來, 常常會把map的key打錯又多花很多時間在debug.

在StackOverflow上查了參數數量的問題, 沒想到還真的有一個很棒的解法, 就是 NSInvocation.

NSInvocation

NSInvocation的使用有一個地方要特別注意, invocation不能使用 allocinit來建立實體, 只能使用invocationWithMethodSignature:來建立object.

object的method
1
2
3
- (void)actionInvokeArgument1:(NSInteger)arg1 arg2:(NSNumber *)arg2 {  NSLog(@"action: arg1 + arg2=%d", arg1 + [arg2 integerValue]); } 

使用Invocation, mehtod所帶入的參數也不用都轉成object, int、BOOL…etc.都可以使用

建立一個NSInvocation實體
1
2
3
SEL mySelector = @selector(actionInvokeArgument1:arg2:); NSMethodSignature* signature1 = [self methodSignatureForSelector:mySelector]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature1]; 

我們先使用NSObject的instance mehtod(也可以使用Class mehtod, 依照個人使用習慣), 先取得一個NSMethodSignature物件, 並建立一個NSInvocation.

NSMethodSignature的numberOfArguments最小值是2; 0跟1是給預設的隱藏參數使用(self & _cmd). 從2開始才是method-spec使用.

這時取得的invocation只有簡單的method可帶入的argument數量, 在正式調用之前你還需要設定targetselector.

設定invocation並調用
1
2
3
4
5
6
7
[invocation setTarget:self]; [invocation setSelector:mySelector]; NSInteger arg1 = 1; NSNumber* arg2 = @2; [invocation setArgument:&arg1 atIndex:2]; [invocation setArgument:&arg2 atIndex:3]; [invocation invoke]; 

使用NSInvocation設定參數時, index要從2開始.

取得回傳值
1
2
3
NSInteger retVal; [invocation getReturnValue:&retVal]; NSLog(@"%d", retVal); 

如果要延遲的話可以這樣使用

delay invoke
1
[invocation performSelector:@selector(invoke) withObject:nil afterDelay:1];

要注意的是, 如果會需要取得回傳值, 可能不適合使用delay的方式~


转载:http://greenchiu.github.io/blog/2013/04/27/yong-nsinvocationlai-qu-dai-performselector/



在ios直接调用某个对象的消息是方法有两种:

一:performselector:withObject:

二:invocation

 

第一种方式比较简单,能完成简单的调用。但是对于>2个的参数或者有返回值的处理,那就需要做些额外工作才能搞定。那么在这种情况下,我们就可以使用NSInvocation来进行这些相对复杂的操作

 

NSInvocation可以处理参数、返回值。会java的人都知道凡是操作,其实NSInvocation就相当于反射操作。

 

 

 

C代码   收藏代码
  1. //方法签名类,需要被调用消息所属的类AsynInvoke ,被调用的消息invokeMethod:  
  2. NSMethodSignature *sig= [[AsynInvoke class] instanceMethodSignatureForSelector:@selector(invokeMethod:)];  
  3. //根据方法签名创建一个NSInvocation  
  4. NSInvocation *invocation=[NSInvocation invocationWithMethodSignature:sig];  
  5. //设置调用者也就是AsynInvoked的实例对象,在这里我用self替代  
  6. [invocation setTarget:self];  
  7. //设置被调用的消息  
  8. [invocation setSelector:@selector(invokeMethod:)];  
  9. //如果此消息有参数需要传入,那么就需要按照如下方法进行参数设置,需要注意的是,atIndex的下标必须从2开始。原因为:0 1 两个参数已经被target 和selector占用  
  10. NSInteger num=10;  
  11. [invocation setArgument:&num atIndex:2];  
  12. //retain 所有参数,防止参数被释放dealloc  
  13. [invocation retainArguments];  
  14. //消息调用  
  15. [invocation invoke];  
  16. //如果调用的消息有返回值,那么可进行以下处理  
  17.   
  18.   
  19.   
  20. //获得返回值类型  
  21. const char *returnType = sig.methodReturnType;  
  22. //声明返回值变量  
  23. id returnValue;  
  24. //如果没有返回值,也就是消息声明为void,那么returnValue=nil  
  25. if( !strcmp(returnType, @encode(void)) ){  
  26.     returnValue =  nil;  
  27. }  
  28. //如果返回值为对象,那么为变量赋值  
  29. else if( !strcmp(returnType, @encode(id)) ){  
  30.     [invocation getReturnValue:&returnValue];  
  31. }  
  32. else{  
  33. //如果返回值为普通类型NSInteger  BOOL  
  34.   
  35.     //返回值长度  
  36.     NSUInteger length = [sig methodReturnLength];  
  37.     //根据长度申请内存  
  38.     void *buffer = (void *)malloc(length);  
  39.     //为变量赋值  
  40.     [invocation getReturnValue:buffer];  
  41.   
  42. //以下代码为参考:具体地址我忘记了,等我找到后补上,(很对不起原作者)  
  43. if( !strcmp(returnType, @encode(BOOL)) ) {  
  44.     returnValue = [NSNumber numberWithBool:*((BOOL*)buffer)];  
  45. }  
  46. else if( !strcmp(returnType, @encode(NSInteger)) ){  
  47.     returnValue = [NSNumber numberWithInteger:*((NSInteger*)buffer)];  
  48. }  
  49.     returnValue = [NSValue valueWithBytes:buffer objCType:returnType];  
  50. }  

 

 

调用步骤:

C代码   收藏代码
  1. - (NSString *) myMethod:(NSString *)param1 withParam2:(NSNumber *)param2{  
  2.       
  3.     NSString *result = @"Objective-C";  
  4.     NSLog(@"Param 1 = %@", param1);  
  5.     NSLog(@"Param 2 = %@", param2);  
  6.     return(result);  
  7. }  
  8.   
  9. - (void) invokeMyMethodDynamically {  
  10.     SEL selector = @selector(myMethod:withParam2:);  
  11.       
  12.     NSMethodSignature *methodSignature = [[self class] instanceMethodSignatureForSelector:selector];  
  13.       
  14.     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];  
  15.       
  16.     [invocation setTarget:self];  
  17.       
  18.     [invocation setSelector:selector];  
  19.   
  20.     NSString *returnValue = nil;  
  21.     NSString *argument1 = @"First Parameter";  
  22.     NSNumber *argument2 = [NSNumber numberWithInt:102];  
  23.       
  24.     [invocation setArgument:&argument1 atIndex:2];  
  25.     [invocation setArgument:&argument2 atIndex:3];  
  26.     [invocation retainArguments];  
  27.     [invocation invoke];  
  28.     [invocation getReturnValue:&returnValue];  
  29.       
  30.     NSLog(@"Return Value = %@", returnValue);  
  31. }  
  32.   
  33.    

 

 

To do this, you need to follow these steps:
1. Form a SEL value using the name of the method and its parameter names (as
explained in Recipe 1.7).
2. Form a method signature of type NSMethodSignature out of your SEL value.
3. Form an invocation of type NSInvocation out of your method signature.
4. Tell the invocation what object you are targeting.
5. Tell the invocation what selector in that object you want to invoke.
6. Assign any arguments, one by one, to the invocation.
7. Invoke the method using the invocation object and demand a return value (if any).


你可能感兴趣的:(用NSInvocation來作為object performSelector的替代方案)