NSInvocation
究竟是如何运作的?有一个很好的介绍吗?
我特别遇到了解以下代码(来自 Mac OS X的Cocoa编程,第3版)如何工作的问题,但是也能够独立于教程示例应用这些概念。代码:
- (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];
}
我得到了它想做的事情。 (顺便说一句,employees
是自定义NSArray
课程的Person
。)
作为一个.NET人,我尝试将不熟悉的Obj-C和Cocoa概念与大致相似的.NET概念联系起来。这类似于.NET的委托概念,但是无类型?
这本书并非100%明确,所以我正在寻找真正的Cocoa / Obj-C专家的补充,同样我的目标是理解简单(-ish)示例下的基本概念。我真的希望能够独立应用这些知识 - 直到第9章,我没有遇到任何困难。但现在......
提前致谢!
答案 0 :(得分:278)
根据Apple's NSInvocation class reference:
NSInvocation
是一个静态渲染的Objective-C消息,也就是说,它是一个变成对象的动作。
并且,在 little 更详细信息中:
信息的概念是客观c哲学的核心。无论何时调用方法或访问某个对象的变量,都会向其发送消息。当您想要在不同的时间点向对象发送消息或多次发送相同的消息时,NSInvocation
会派上用场。 NSInvocation
允许您描述您要发送的消息,然后调用(实际上将其发送到目标对象)。
例如,假设您要将字符串添加到数组中。您通常会发送addObject:
消息,如下所示:
[myArray addObject:myString];
现在,假设你想使用NSInvocation
在其他时间点发送此消息:
首先,您要准备一个NSInvocation
对象,以便与NSMutableArray
的{{1}}选择器一起使用:
addObject:
接下来,您将指定将消息发送到的对象:
NSMethodSignature * mySignature = [NSMutableArray
instanceMethodSignatureForSelector:@selector(addObject:)];
NSInvocation * myInvocation = [NSInvocation
invocationWithMethodSignature:mySignature];
指定要发送给该对象的消息:
[myInvocation setTarget:myArray];
并填写该方法的任何参数:
[myInvocation setSelector:@selector(addObject:)];
请注意,对象参数必须通过指针传递。感谢您Ryan McCuaig指出这一点,有关详细信息,请参阅Apple's documentation。
此时,[myInvocation setArgument:&myString atIndex:2];
是一个完整的对象,描述了可以发送的消息。要实际发送消息,您可以致电:
myInvocation
此最后一步将导致发送消息,基本上执行[myInvocation invoke];
。
将其视为发送电子邮件。您打开一个新电子邮件([myArray addObject:myString];
对象),填写要将其发送给的人(对象)的地址,键入收件人的消息(指定NSInvocation
和参数),然后点击“发送”(致电selector
)。
有关详细信息,请参阅Using NSInvocation。 如果上述内容无效,请参阅Using NSInvocation。
invoke
使用NSUndoManager
个对象,以便反向命令。从本质上讲,你正在做的是创建一个NSInvocation
对象来说:“嘿,如果你想撤消我刚刚做的事情,请将这条消息发送给该对象,并使用这些参数”。您将NSInvocation
对象提供给NSInvocation
,并将该对象添加到可撤消操作数组中。如果用户调用“撤消”,NSUndoManager
只会查找数组中最近的操作,并调用存储的NSUndoManager
对象来执行必要的操作。
有关详细信息,请参阅Registering Undo Operations。
答案 1 :(得分:46)
以下是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"];
时 - 方法将:
最后,你会得到一个类似的打印输出:
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];
答案 2 :(得分:5)
答案 3 :(得分:0)
我构建了一个使用NSInvocation调用各种方法类型的简单示例。
我在使用obj_msgSend
调用多个参数时遇到问题