当前位置:  开发笔记 > 编程语言 > 正文

NSInvocation for Dummies?

如何解决《NSInvocationforDummies?》经验,为你挑选了3个好方法。

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,employeesNSArray一个自定义Person类.)

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

这本书并非100%明确,所以我正在寻找一些来自真正的Cocoa/Obj-C专家的补充,同样我的目标是理解简单(-ish)示例下的基本概念.我真的希望能够独立应用这些知识 - 直到第9章,我没有遇到任何困难.但现在 ...

提前致谢!



1> e.James..:

根据Apple的NSInvocation类引用:

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

而且,更详细一点:

信息的概念是客观c哲学的核心.无论何时调用方法或访问某个对象的变量,都会向其发送消息.NSInvocation当您想要在不同的时间点向对象发送消息或多次发送相同的消息时,它会派上用场.NSInvocation允许您描述要发送的消息,然后稍后调用它(实际上将其发送到目标对象).


例如,假设您要将字符串添加到数组中.您通常会发送addObject:如下消息:

[myArray addObject:myString];

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

首先,你要准备一个NSInvocation与使用对象NSMutableArrayaddObject:选择:

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对象以执行必要的操作.

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


为了澄清Ryan的注释,索引0保留用于"self",索引1保留用于"_cmd"(有关详细信息,请参阅e.James链接).所以你的第一个参数放在索引2,第二个参数放在索引3等等......
对其他优秀答案的一个小修正......你必须在`setArgument:atIndex:`中传递一个指向对象的指针,所以arg赋值实际应该是`[myInvocation setArgument:&myString atIndex:2]`.
我不明白为什么我们必须调用setSelector,因为我们已经在mySignature中指定了选择器.
@Gleno:NSInvocation非常灵活.您实际上可以设置与方法签名匹配的任何选择器,因此您不必使用用于创建方法签名的相同选择器.在这个例子中,您可以轻松地执行setSelector:@selector(removeObject :),因为它们共享相同的方法签名.
@haroldcampbell:我们要打电话给什么?

2> Dave Gallagh..:

这是运行中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];


有趣的是,即使使用`invocationWithMethodSignature:`初始化程序,你仍然需要调用`setSelector:`.这似乎是多余的,但我刚刚测试过,这是必要的.

3> Casebash..:

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

推荐阅读
贴进你的心聆听你的世界
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有