NSInvocation空参数

8

我该如何(或者说我是否可以)向NSInvocation对象传递nil参数?

我尝试过这样做:

NSMethodSignature* signature = [AClass instanceMethodSignatureForSelector:@selector(aMethod:theOtherArg:)];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: signature];

[invocation setTarget: aTargetObj];
[invocation setSelector: @selector(aMethod:theOtherArg:)];

/* I've tried both this way */
AnObj* arg1 = nil;
AnotherObj* arg2 = nil;
[invocation setArgument: &arg1 atIndex:2];
[invocation setArgument: &arg2 atIndex:3];

/* and this way */
//[invocation setArgument: nil atIndex:2];
//[invocation setArgument: nil atIndex:3];

NSInvocationOperation* operation = [[NSInvocationOperation alloc] initWithInvocation:invocation];
//opQueue is an NSOperationQueue object
[opQueue addOperation:operation];

第一种方法会崩溃,并提示以下消息:
Thread 0 Crashed:
0   libSystem.B.dylib              0x927c1f10 strlen + 16
1   com.apple.CoreFoundation       0x92dd1654 __NSI3 + 500
2   com.apple.CoreFoundation       0x92dd1994 -[NSInvocation retainArguments] + 132
3   com.apple.Foundation           0x96a50c5e -[NSInvocationOperation initWithInvocation:] + 78

第二种方法会导致崩溃,出现以下错误提示信息:
Error: Exiting due to caught object *** -[NSInvocation setArgument:atIndex:]: NULL address argument

非常感谢您提前的帮助!

(相关的IT技术内容需要您提供具体的翻译)
2个回答

10

是的,你可以传递nil参数。更准确地说,你可以传递一个有效指针,其内容为nil。

我无法重现你的特定问题。这是我用来测试的代码。可能是你程序中的其他因素导致了错误。

#import <Foundation/Foundation.h>

@interface Person : NSObject {
    NSString *name;
    NSUInteger age;
}
- (void)setName:(NSString *)personName age:(NSNumber *)personAge;
@end

@implementation Person
- (void)setName:(NSString *)personName age:(NSNumber *)personAge {
    NSLog(@"setName:%@ age:%@", personName, personAge);
    name = [personName copy];
    age = [personAge unsignedIntegerValue];
}
@end

int main() {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];            
    Person *person = [Person new];

    SEL sel = @selector(setName:age:);

    NSMethodSignature *sig = [Person instanceMethodSignatureForSelector:sel];
    NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];

    [inv setTarget:person];
    [inv setSelector:sel];

    NSString *name = nil;
    NSNumber *age = nil;

    [inv setArgument:&name atIndex:2];
    [inv setArgument:&age atIndex:3];

    // [inv retainArguments];
    // [inv invoke];

    NSInvocationOperation *op = [[[NSInvocationOperation alloc] initWithInvocation:inv] autorelease];
    NSOperationQueue *queue = [NSOperationQueue new];
    [queue addOperation:op];

    [pool release];
    return 0;
}

我还测试了不使用NSInvocationOperation并直接发送-retainArguments,因为这似乎是触发您错误的原因。

值得注意的是,您的第二种方法不起作用,因为-[NSInvocation setArgument:atIndex:]需要一个指向将被复制的缓冲区的有效内存地址。对于对象参数,缓冲区必须包含对象的地址。对象的地址可以为零,但缓冲区的地址必须有效。


你说得对。问题与设置/不设置参数为nil值无关。我的一个非nil参数是一个双精度原始数组,我忘记在传递到调用对象之前将其转换为双指针。 - Yahya Cahyadi

3
您可以通过简单地不调用setArgument:atIndex:来实现此目的,以保持您希望保留为nil的参数索引。
然而,如果您已经将该参数设置为除nil之外的某些内容,并且希望将其更改回nil,则一些快速测试似乎表明您无法这样做。我可能是错的,但看起来您需要创建一个新的调用对象,并手动将所有所需值复制到其中,减去您希望保持为nil的参数。

你是对的。我忘了提到,我想过并尝试了不为nil参数调用setArgument:atIndex:方法。但问题根本与我的nil参数无关。 - Yahya Cahyadi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接