這是幾個ios fundamental design pattern之一
然而有時候我們會希望把幾個target action放進array或是dictionary之中,或是直接當作member variable
但是SEL並不是一般繼承於NSObject的class,所以不能直接放進Array當中
當然我們可以用
NSStringFromSelector(SEL aSelector)
以及
NSSelectorFromString(NSString *aSelectorName)
讓SEL可以跟NSString*互換
但是怎麼都覺得不是很方便
我就在想有沒有比較漂亮的方法
我找了一下iOS foundation library,當中就屬
NSInvocation
最合適這個Class就是把target, action, arguments包在一起,
當我們呼叫
-[NSInvocation invoke]
就會呼叫這個target的selector,並且傳進這些arguments...
太棒了,這就是我要的class!!
但是NSInvocation的產生方式實在不怎麼好用,如果我們要把這些東西全部丟進去
那我們需要如下面這段code
NSMethodSignature* sig = [[target class] instanceMethodSignatureForSelector:@selector(mySelector:]; NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sig]; [invocation setTarget:target]; [invocation setSelector:selector];看起來很不友善吧...
不好用的話,那我們就來自己動手做Category吧...
NSInvocation+PopcornyLu.h / NSInvocation+PopcornyLu.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
@interface NSInvocation (PopcornyLu) | |
+ (NSInvocation*) invocationWithTarget:(id)target | |
selector:(SEL)selector; | |
+ (NSInvocation*) invocationWithTarget:(id)target | |
selector:(SEL)selector | |
arguments:(id)firstArg, ... NS_REQUIRES_NIL_TERMINATION; | |
@end | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "NSInvocation+PopcornyLu.h" | |
@implementation NSInvocation (PopcornyLu) | |
+ (NSInvocation*) invocationWithTarget:(id)target | |
selector:(SEL)selector | |
{ | |
NSMethodSignature* sig = [[target class] instanceMethodSignatureForSelector:selector]; | |
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sig]; | |
[invocation setTarget:target]; | |
[invocation setSelector:selector]; | |
return invocation; | |
} | |
+ (NSInvocation*) invocationWithTarget:(id)target | |
selector:(SEL)selector | |
arguments:(id)firstArg, ... | |
{ | |
NSMethodSignature* sig = [[target class] instanceMethodSignatureForSelector:selector]; | |
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sig]; | |
[invocation setTarget:target]; | |
[invocation setSelector:selector]; | |
va_list args; | |
id arg; | |
va_start(args, firstArg); | |
NSUInteger index = 2; | |
for(arg = firstArg; | |
arg != nil; | |
arg = va_arg(args, id)) | |
{ | |
[invocation setArgument:&arg atIndex:index++]; | |
} | |
va_end(args); | |
return invocation; | |
} | |
@end |
這邊我提供了兩個Class messages來產生NSInvocation
其中第二個可以提供不定量的arguments.
使用上很簡單
// Put invocations in array NSArray* myActions = [NSArray arrayWithObjects: [NSInvocation invocationWithTarget:self selector:@selector(action1)], [NSInvocation invocationWithTarget:self selector:@selector(action2:) arguments:@"foo", nil], [NSInvocation invocationWithTarget:self selector:@selector(action3WithFoo:andBar:) arguments:@"foo", @"bar", nil], nil]; // invoke all the actions for(NSInvocation* invocation in myActions) { [invocation invoke]; }
[補充]
當然也可以用block來取代target/action
以上面的例子我們可以用block來實作
NSArray* myActions2 = [NSArray arrayWithObjects: [[^(void){ [self action1]; } copy] autorelease], [[^(void){ [self action2:@"foo"]; } copy] autorelease], [[^(void){ [self action3WithFoo:@"foo" andBar:@"bar"]; } copy] autorelease], nil]; // invoke all the actions for(void(^invocation)() in myActions2) { invocation(); }
但是缺點是code比較凌亂一點,然後action1, action2:, action3WithFoo:withBar這些message都要在前面要先定義
稍微麻煩了點
但基本上Target/Action或是Block都有其方便之處
像這邊我希望array creation的地方不要寫太多code,所以我會選擇用target/action
但有時候使用上會用inline block比較方便,那我就用block
還在忙報告,未學到
回覆刪除遲點再看- -"