NSInvocation
工作究竟如何?有一个很好的介绍吗?
我特别关注以下代码(来自Cocoa Programming for Mac OS X,3rd Edition)的工作原理,但是也可以独立于教程示例应用这些概念.代码:
- (void)insertObject:(Person *)p inEmployeesAtIndex:(int)index { NSLog(@"adding %@ to %@", p, employees); // Add inverse of this operation to undo stack NSUndoManager *undo = [self undoManager]; [[undo prepareWithInvocationTarget:self] removeObjectFromEmployeesAtIndex:index]; if (![undo isUndoing]) [undo setActionName:@"Insert Person"]; // Finally, add person to the array [employees insertObject:p atIndex:index]; } - (void)removeObjectFromEmployeesAtIndex:(int)index { Person *p = [employees objectAtIndex:index]; NSLog(@"removing %@ from %@", p, employees); // Add inverse of this operation to undo stack NSUndoManager *undo = [self undoManager]; [[undo prepareWithInvocationTarget:self] insertObject:p inEmployeesAtIndex:index]; if (![undo isUndoing]) [undo setActionName:@"Delete Person"]; // Finally, remove person from array [employees removeObjectAtIndex:index]; }
我得到了它想做的事情.(BTW,employees
是NSArray
一个自定义Person
类.)
作为一个.NET人,我尝试将不熟悉的Obj-C和Cocoa概念与大致相似的.NET概念联系起来.这类似于.NET的委托概念,但是无类型?
这本书并非100%明确,所以我正在寻找一些来自真正的Cocoa/Obj-C专家的补充,同样我的目标是理解简单(-ish)示例下的基本概念.我真的希望能够独立应用这些知识 - 直到第9章,我没有遇到任何困难.但现在 ...
提前致谢!
根据Apple的NSInvocation类引用:
An
NSInvocation
是一个静态呈现的Objective-C消息,也就是说,它是一个变成对象的动作.
而且,更详细一点:
信息的概念是客观c哲学的核心.无论何时调用方法或访问某个对象的变量,都会向其发送消息.NSInvocation
当您想要在不同的时间点向对象发送消息或多次发送相同的消息时,它会派上用场.NSInvocation
允许您描述要发送的消息,然后稍后调用它(实际上将其发送到目标对象).
例如,假设您要将字符串添加到数组中.您通常会发送addObject:
如下消息:
[myArray addObject:myString];
现在,假设您想用它NSInvocation
来在其他时间点发送此消息:
首先,你要准备一个NSInvocation
与使用对象NSMutableArray
的addObject:
选择:
NSMethodSignature * mySignature = [NSMutableArray instanceMethodSignatureForSelector:@selector(addObject:)]; NSInvocation * myInvocation = [NSInvocation invocationWithMethodSignature:mySignature];
接下来,您将指定将消息发送到的对象:
[myInvocation setTarget:myArray];
指定要发送给该对象的消息:
[myInvocation setSelector:@selector(addObject:)];
并填写该方法的任何参数:
[myInvocation setArgument:&myString atIndex:2];
请注意,对象参数必须通过指针传递.感谢Ryan McCuaig指出这一点,请参阅Apple的文档了解更多详情.
此时,myInvocation
是一个完整的对象,描述了可以发送的消息.要实际发送消息,您可以致电:
[myInvocation invoke];
最后一步将导致消息被发送,基本上是执行[myArray addObject:myString];
.
把它想象成发送电子邮件.您打开一个新的电子邮件(NSInvocation
对象),填写要将其发送给的人(对象)的地址,键入收件人的消息(指定selector
和参数),然后单击"发送"(调用)invoke
).
有关更多信息,请参阅使用NSInvocation.如果上述操作无效,请参阅使用NSInvocation.
NSUndoManager
使用NSInvocation
对象,以便它可以反转命令.从本质上讲,你正在做的是创建一个NSInvocation
对象来说:"嘿,如果你想要撤消我刚刚做的事情,请将这条消息发送给该对象,并使用这些参数".您将NSInvocation
对象赋予NSUndoManager
,并将该对象添加到可撤消操作的数组中.如果用户调用"撤消",则NSUndoManager
只需查找数组中的最新操作,并调用存储的NSInvocation
对象以执行必要的操作.
有关更多详细信息,请参阅注册撤消操作.
这是运行中NSInvocation的一个简单示例:
- (void)hello:(NSString *)hello world:(NSString *)world { NSLog(@"%@ %@!", hello, world); NSMethodSignature *signature = [self methodSignatureForSelector:_cmd]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; [invocation setTarget:self]; // index 0 (hidden) [invocation setSelector:_cmd]; // index 1 (hidden) [invocation setArgument:&hello atIndex:2]; // index 2 [invocation setArgument:&world atIndex:3]; // index 3 // NSTimer's always retain invocation arguments due to their firing delay. Release will occur when the timer invalidates itself. [NSTimer scheduledTimerWithTimeInterval:1 invocation:invocation repeats:NO]; }
调用时 - [self hello:@"Hello" world:@"world"];
- 方法将:
打印"Hello world!"
为自己创建一个NSMethodSignature.
创建并填充NSInvocation,调用自身.
将NSInvocation传递给NSTimer
计时器将在(大约)1秒内触发,导致使用其原始参数再次调用该方法.
重复.
最后,你会得到这样的打印输出:
2010-07-11 17:48:45.262 Your App[2523:a0f] Hello world! 2010-07-11 17:48:46.266 Your App[2523:a0f] Hello world! 2010-07-11 17:48:47.266 Your App[2523:a0f] Hello world! 2010-07-11 17:48:48.267 Your App[2523:a0f] Hello world! 2010-07-11 17:48:49.268 Your App[2523:a0f] Hello world! 2010-07-11 17:48:50.268 Your App[2523:a0f] Hello world! 2010-07-11 17:48:51.269 Your App[2523:a0f] Hello world! ...
当然,目标对象self
必须继续存在,NSTimer才能将NSInvocation发送给它.例如,Singleton对象,或在应用程序持续时间内存在的AppDelegate.
更新:
如上所述,当您将NSInvocation作为参数传递给NSTimer时,NSTimer会自动保留所有NSInvocation的参数.
如果您没有将NSInvocation作为参数传递给NSTimer,并计划让它保持一段时间,则必须调用其-retainArguments
方法.否则,在调用调用之前可能会释放其参数,最终导致代码崩溃.这是怎么做的:
NSMethodSignature *signature = ...; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; id arg1 = ...; id arg2 = ...; [invocation setTarget:...]; [invocation setSelector:...]; [invocation setArgument:&arg1 atIndex:2]; [invocation setArgument:&arg2 atIndex:3]; [invocation retainArguments]; // If you do not call this, arg1 and arg2 might be deallocated. [self someMethodThatInvokesYourInvocationEventually:invocation];
您可以尝试在这里使用更好的库:http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html