多此一舉的呼叫?從 NSInvocation 看命令模式

命令模式
命令模式是一種將方法呼叫封裝為物件的設計模式,在iOS中具體實現為NSInvocation,你可以從NSInvocation的設計中體會命令模式的具體實現思路。NSInvocation將執行一個方法的所有一切資訊都打包在一起,分為四部分:
目標 選擇器 方法簽名 引數
下面來看看NSInvocation 的實際使用:
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSMethodSignature *sig = [self methodSignatureForSelector:@selector(doSomeThing:)]; // 方法簽名 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; invocation.target = self; // 目標 invocation.selector = @selector(doSomeThing:); // 選擇器 NSString *arg1 = @"Xishi"; [invocation setArgument:&arg1 atIndex:2]; // 引數 [invocation invoke]; // 執行 } - (void)doSomeThing:(NSString *)arg1 { NSLog(@"doSomeThing被呼叫,arg1 = %@", arg1); } @end
傳遞引數時,第二個引數 atIndex
從 2開始
, index 0~1
分別是 目標
與 選擇器
,由NSInvocation自動傳遞,所以假設有3個引數的方法:
- (void)doSomeThing:(NSString *)arg1 arg2:(NSString *)arg2 arg3:(NSString *)arg3 { NSLog(@"doSomeThing被呼叫,arg1 = %@, arg2 = %@, arg3 = %@", arg1, arg2, arg3); }
執行3個引數的方法如下:
NSMethodSignature *sig = [self methodSignatureForSelector:@selector(doSomeThing:arg2:arg3:)]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; invocation.target = self; invocation.selector = @selector(doSomeThing:arg2:arg3:); NSString *arg1 = @"Xishi"; NSString *arg2 = @"is"; NSString *arg3 = @"Developer"; [invocation setArgument:&arg1 atIndex:2]; [invocation setArgument:&arg2 atIndex:3]; [invocation setArgument:&arg3 atIndex:4]; [invocation invoke];
執行結果如下:
doSomeThing被呼叫,arg1 = Xishi, arg2 = is, arg3 = Developer
由於本例子是呼叫當前類(self)的方法,顯得比直接呼叫方法繁瑣,不過可以看出,實際呼叫者只需要獲得NSInvocation物件,呼叫invoke即可執行方法,並不需要知道具體執行物件,這對如選單這樣的“執行檢視”提供了一種解耦方式,也是 forwardInvocation 和 undo 系統的基礎。