objective-c 傻瓜的NSInvocation?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/313400/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 20:55:44  来源:igfitidea点击:

NSInvocation for Dummies?

objective-ccocoaundo-redo

提问by John Rudy

How exactly does NSInvocationwork? Is there a good introduction?

究竟是如何NSInvocation工作的?有好的介绍吗?

I'm specifically having issues understanding how the following code (from Cocoa Programming for Mac OS X, 3rd Edition) works, but then also be able to apply the concepts independently of the tutorial sample. The code:

我在理解以下代码(来自Cocoa Programming for Mac OS X,第 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];
}

I get what it's trying to do. (BTW, employeesis an NSArrayof a custom Personclass.)

我明白它想要做什么。(顺便说一句,employeesNSArray一个自定义Person类。)

Being a .NET guy, I try to associate unfamiliar Obj-C and Cocoa concepts to roughly analogous .NET concepts. Is this similar to .NET's delegate concept, but untyped?

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

This isn't 100% clear from the book, so I'm looking for something supplemental from real Cocoa/Obj-C experts, again with the goal that I understand the fundamental concept beneath the simple(-ish) example. I'm really looking to be able to independently apply the knowledge -- up until chapter 9, I was having no difficulty doing that. But now ...

这在书中并不是 100% 清楚,所以我正在寻找真正的 Cocoa/Obj-C 专家的补充内容,再次以我理解简单(-ish)示例下的基本概念为目标。我真的希望能够独立应用这些知识——直到第 9 章,我都没有困难做到这一点。但现在 ...

Thanks in advance!

提前致谢!

回答by e.James

According to Apple's NSInvocation class reference:

根据Apple 的 NSInvocation 类参考

An NSInvocationis an Objective-C message rendered static, that is, it is an action turned into an object.

AnNSInvocation是一个静态渲染的 Objective-C 消息,也就是说,它是一个动作变成了一个对象。

And, in a littlemore detail:

而且,更详细一点

The concept of messages is central to the objective-c philosophy. Any time you call a method, or access a variable of some object, you are sending it a message. NSInvocationcomes in handy when you want to send a message to an object at a different point in time, or send the same message several times. NSInvocationallows you to describethe message you are going to send, and then invokeit (actually send it to the target object) later on.

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



For example, let's say you want to add a string to an array. You would normally send the addObject:message as follows:

例如,假设您想向数组中添加一个字符串。您通常会addObject:按如下方式发送消息:

[myArray addObject:myString];


Now, let's say you want to use NSInvocationto send this message at some other point in time:

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

First, you would prepare an NSInvocationobject for use with NSMutableArray's addObject:selector:

首先,您将准备一个NSInvocationNSMutableArrayaddObject:选择器一起使用的对象:

NSMethodSignature * mySignature = [NSMutableArray
    instanceMethodSignatureForSelector:@selector(addObject:)];
NSInvocation * myInvocation = [NSInvocation
    invocationWithMethodSignature:mySignature];

Next, you would specify which object to send the message to:

接下来,您将指定要将消息发送到哪个对象:

[myInvocation setTarget:myArray];

Specify the message you wish to send to that object:

指定要发送给该对象的消息:

[myInvocation setSelector:@selector(addObject:)];

And fill in any arguments for that method:

并填写该方法的任何参数:

[myInvocation setArgument:&myString atIndex:2];

Note that object arguments must be passed by pointer. Thank you to Ryan McCuaigfor pointing that out, and please see Apple's documentationfor more details.

请注意,对象参数必须通过指针传递。感谢Ryan McCuaig指出这一点,有关更多详细信息,请参阅Apple 的文档

At this point, myInvocationis a complete object, describing a message that can be sent. To actually send the message, you would call:

此时,myInvocation是一个完整的对象,描述了一个可以发送的消息。要实际发送消息,您可以调用:

[myInvocation invoke];

This final step will cause the message to be sent, essentially executing [myArray addObject:myString];.

这最后一步将导致消息被发送,本质上是执行[myArray addObject:myString];.

Think of it like sending an email. You open up a new email (NSInvocationobject), fill in the address of the person (object) who you want to send it to, type in a message for the recipient (specify a selectorand arguments), and then click "send" (call invoke).

把它想象成发送电子邮件。您打开一封新电子邮件(NSInvocation对象),填写要发送给的人(对象)的地址,输入收件人的消息(指定 aselector和参数),然后单击“发送”(呼叫invoke)。

See Using NSInvocationfor more information. See Using NSInvocationif the above is not working.

有关更多信息,请参阅使用 NSInvocation。如果上述方法不起作用,请参阅使用 NSInvocation



NSUndoManageruses NSInvocationobjects so that it can reversecommands. Essentially, what you are doing is creating an NSInvocationobject to say: "Hey, if you want to undo what I just did, send this message to that object, with these arguments". You give the NSInvocationobject to the NSUndoManager, and it adds that object to an array of undoable actions. If the user calls "Undo", NSUndoManagersimply looks up the most recent action in the array, and invokes the stored NSInvocationobject to perform the necessary action.

NSUndoManager使用NSInvocation对象以便它可以反转命令。本质上,您正在做的是创建一个NSInvocation对象来说:“嘿,如果您想撤消我刚刚做的事情,请将此消息发送给该对象,并带有这些参数”。您将NSInvocation对象提供给NSUndoManager,它会将该对象添加到一组可撤消的操作中。如果用户调用“撤消”,NSUndoManager只需在数组中查找最近的操作,并调用存储的NSInvocation对象来执行必要的操作。

See Registering Undo Operationsfor more details.

有关更多详细信息,请参阅注册撤消操作

回答by Dave Gallagher

Here's a simple example of NSInvocation in action:

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

When called - [self hello:@"Hello" world:@"world"];- the method will:

调用时 - [self hello:@"Hello" world:@"world"];- 该方法将:

  • Print "Hello world!"
  • Create an NSMethodSignature for itself.
  • Create and populate an NSInvocation, calling itself.
  • Pass the NSInvocation to an NSTimer
  • The timer will fire in (approximately) 1 second, causing the method to be called again with its original arguments.
  • Repeat.
  • 打印“世界你好!”
  • 为自己创建一个 NSMethodSignature。
  • 创建并填充一个 NSInvocation,调用自身。
  • 将 NSInvocation 传递给 NSTimer
  • 计时器将在(大约)1 秒内触发,从而使用其原始参数再次调用该方法。
  • 重复。

In the end, you'll get a printout like so:

最后,你会得到这样的打印输出:

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!
...

Of course, the target object selfmust continue to exist for the NSTimer to send the NSInvocation to it. For example, a Singletonobject, or an AppDelegate which exists for the duration of the application.

当然,目标对象self必须继续存在,以便 NSTimer 向它发送 NSInvocation。例如,一个Singleton对象,或一个在应用程序期间存在的 AppDelegate。



UPDATE:

更新:

As noted above, when you pass an NSInvocation as an argument to an NSTimer, the NSTimer automatically retains all of the NSInvocation's arguments.

如上所述,当您将 NSInvocation 作为参数传递给 NSTimer 时,NSTimer 会自动保留所有 NSInvocation 的参数。

If you are not passing an NSInvocation as an argument to an NSTimer, and plan on having it stick around for a while, you must call its -retainArgumentsmethod. Otherwise its arguments may be deallocated before the invocation is invoked, eventually causing your code to crash. Here's how to do it:

如果您没有将 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];

回答by Casebash

You could try just using the library here which is much nicer: http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

您可以尝试在这里使用更好的库:http: //cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

回答by brian.clear

I build a simple example of calling various method types with NSInvocation.

我构建了一个使用 NSInvocation 调用各种方法类型的简单示例。

I had problems calling multiple params using obj_msgSend

我在使用 obj_msgSend 调用多个参数时遇到问题

https://github.com/clearbrian/NSInvocation_Runtime

https://github.com/clearbrian/NSInvocation_Runtime