NSInvocation为傻瓜?

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]; } 

我得到了它想要做的事情。 (顺便说一下, employees是自定义Person类的NSArray 。)

作为一个.NET人,我尝试将不熟悉的Obj-C和Cocoa概念联系到大致类似的.NET概念。 这是类似于.NET的委托概念,但无types?

这本书不是100%清楚的,所以我正在寻找来自真正的cocoa/ Obj-C专家的补充,同样的目标是我理解简单(示例)下面的基本概念。 我真的希望能够独立应用知识,直到第九章,我毫不费力地做到这一点。 但现在 …

提前致谢!

根据苹果的NSInvocation类参考 :

NSInvocation是一个呈现静态的Objective-C消息,也就是说,它是一个变成对象的动作。

而且,再详细一点

消息的概念是客观哲学的核心。 任何时候你调用一个方法,或者访问某个对象的variables,你都会发送一条消息。 NSInvocation派上用场,当你想发送一个消息到一个对象在不同的​​时间点,或者多次发送相同的消息。 NSInvocation允许你描述你要发送的消息,然后调用它(实际上发送给目标对象)。


例如,假设您想要将一个string添加到数组中。 您通常会发送addObject:消息,如下所示:

 [myArray addObject:myString]; 

现在,假设您想在其他时间点使用NSInvocation发送此消息:

首先,你需要准备一个NSInvocation对象来与NSMutableArrayaddObject: selector一起使用:

 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对象,以便它可以反转命令。 从本质上讲,你在做的是创build一个NSInvocation对象来说:“嘿,如果你想撤消我刚刚做的,用这些参数发送这个消息到该对象。 您将NSInvocation对象赋予NSUndoManager ,并将该对象添加到可NSUndoManager操作的数组中。 如果用户调用“Undo”, 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!”
  • 为自己创build一个NSMethodSignature。
  • 创build并填充一个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作为parameter passing给NSTimer时,NSTimer会自动保留所有NSInvocation的参数。

如果你没有把一个NSInvocation作为一个parameter passing给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

我用NSInvocation构build了一个调用各种方法types的简单例子。

我有问题使用obj_msgSend调用多个参数

https://github.com/clearbrian/NSInvocation_Runtime