8

How do I (or can I even) pass a nil argument to an NSInvocation object?

I tried to do this:

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

The first approach will crash with this message:

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

The second approach will crash with this message:

Error: Exiting due to caught object *** -[NSInvocation setArgument:atIndex:]: NULL address argument

Thanks in advance for any help!

Yahya Cahyadi
  • 524
  • 1
  • 4
  • 12

2 Answers2

10

Yes, you can pass nil arguments. More precisely, you can pass a valid pointer whose contents are nil.

I haven't been able to reproduce your particular problem. Here's the code I've used to test it. There's probably something else in your program that's causing the error.

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

I've also tested it without using NSInvocationOperation and sending -retainArguments directly, since that's what seems to be triggering your error.

For the record, your second approach won't work since -[NSInvocation setArgument:atIndex:] expects a valid memory address pointing to a buffer which will be copied. In the case of object arguments, the buffer must contain the address of the object. The address of the object can be zero but the address of the buffer must be valid.

  • You are right. The problem has nothing to do with setting / not setting the arguments with nil values. One of my non-nil arguments is a double primitive array and I forgot to cast it into a double pointer before passing it into the invocation object. – Yahya Cahyadi Dec 29 '10 at 16:55
3

You can accomplish this by simply not calling setArgument:atIndex: for the argument index that you wish to remain nil.

However, if you have already set that argument to something besides nil, and wish to change it back to nil, a few quick tests seem to indicate that you just can't. I could be wrong, but it looks like you would have to create a new invocation object and manually copy all the desired values into it, minus the argument that you wish to remain nil.

Endemic
  • 1,540
  • 11
  • 10
  • You are right. I forgot to mention that I thought of, and actually tried, just not calling `setArgument:atIndex:` for the nil args. The problem was not related to my nil arguments at all. – Yahya Cahyadi Dec 29 '10 at 16:53