用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的方式~

Comments