49

I can't figure out why I get

use of undeclared identifier _cmd  did you mean rcmd

on the line where NSAssert is.

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int x = 10;

    NSAssert(x > 11, @"x should be greater than %d", x);

    [pool drain];
    return 0;
}
foho
  • 779
  • 9
  • 20

5 Answers5

112

Inside every Objective-c method there are two hidden variables id self and SEL _cmd

so

- (void)foo:(id)bar;

is really

void foo(id self, SEL _cmd, id bar) { ... }

and when you call

[someObject foo:@"hello world"]

it is actually

foo( someObject, @selector(foo), @"hello world")

If you cmd-click on NSAssert to jump to it's definition you will see that it is a macro that uses the hidden _cmd variable of the method you are calling it from. This means that if you are not inside an Objective-c method (perhaps you are in 'main'), therefore you don't have a _cmd argument, you cannot use NSAssert.

Instead you can use the alternative NSCAssert.

Robert
  • 37,670
  • 37
  • 171
  • 213
hooleyhoop
  • 9,128
  • 5
  • 37
  • 58
31

NSAssert is only meant to be used within Objective-C methods. Since main is a C function, use NSCAssert instead.

highlycaffeinated
  • 19,729
  • 9
  • 60
  • 91
1

Try to replace

NSAssert(x > 11, [NSString stringWithFormat:@"x should be greater than %d", x]);

with

NSCAssert(x > 11, [NSString stringWithFormat:@"x should be greater than %d", x]);

arun.s
  • 1,528
  • 9
  • 12
0

TL;DR - stick with stray NSAssert() - don't try this in production

Original code

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int x = 10;

    NSAssert(x > 11, @"x should be greater than %d", x);

    [pool drain];
    return 0;
}

Build failure

 Compiling file hello.m ...
hello.m:9:5: error: use of undeclared identifier '_cmd'
    NSAssert(x > 11, @"x should be greater than %d", x);
    ^
/usr/include/Foundation/NSException.h:450:32: note: expanded from macro 'NSAssert'
        handleFailureInMethod: _cmd                             \
                               ^
hello.m:9:5: error: use of undeclared identifier 'self'
/usr/include/Foundation/NSException.h:451:17: note: expanded from macro 'NSAssert'
        object: self                                            \
                ^
2 errors generated.

Based on explanation by @hooleyhoop @Robert and id self SEL, the following dirty hack may be applicable if I insist on using NSAssert() instead of NSCAssert()

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int x = 10;

    // Dirty hack
    SEL _cmd=NULL;
    NSObject *self=NULL;

    NSAssert(x > 11, @"x should be greater than %d", x);

    [pool drain];
    return 0;
}

Build & run

 Compiling file hello.m ...
 Linking tool hello ...
2021-03-04 21:25:58.035 hello[39049:39049] hello.m:13  Assertion failed in (null)(instance), method (null).  x should be greater than 10
./obj/hello: Uncaught exception NSInternalInconsistencyException, reason: hello.m:13  Assertion failed in (null)(instance), method (null).  x should be greater than 10

Hooray it works! But, alas, please stay away from it :)

Darren Ng
  • 373
  • 5
  • 12
0

You have to wrap your string in a NSString class if you want to use format parameters. That is because @"" is a default constructor for a plain NSString. The way it is written now gives a third parameter to the NSAssert function and messes with it.

NSAssert(x > 11, [NSString stringWithFormat:@"x should be greater than %d", x]);
Alexander
  • 8,117
  • 1
  • 35
  • 46