12

I've just been bitten by an annoying bug that was made obscure by the "send message to nil is ok" behaviour in Objective-C.

I've seen Sending a message to nil?, and the consensus seems to be 'thats how we roll' in Objective-C.

Now, maybe I don't have enough experience in Objective-C, but it seems like it would be useful to trap this, because I can't think of a good reason why this should be happening most of the time. However, It could just be a coding idiom I'm not used to yet.

So other than checking for nil everywhere like so:

assert( object != nil );
[object message];

Is there a way to make the runtime trap this condition, and warn when object is nil?

Community
  • 1
  • 1
Justicle
  • 14,761
  • 17
  • 70
  • 94
  • 2
    Why not try to write in an Objective-C idiom, instead of porting Java idioms to Objective-C? – runako May 27 '09 at 06:20
  • 1
    I wouldn't know, I don't code in Java. Clearly other people have found this useful (see answer below). – Justicle May 27 '09 at 06:28
  • 1
    s/Java/another programming language/. Learning the native idioms of any environment will generally make you more productive in that environment. Don't fight your tools. – runako May 29 '09 at 16:36
  • 5
    I'm just asking. That's what stackoverflow is for. – Justicle May 30 '09 at 08:05

2 Answers2

9

You could use a ObjC undocumented trick or dtrace (see the comments for the dtrace solution).

pid$1::objc_msgSend:entry
/arg0==0/
{
  ustack();
}
diciu
  • 29,133
  • 4
  • 51
  • 68
4

nil messaging is used very commonly in ObjC. Folks can fight over whether this is good or bad; it's just something you have to get used to. If you try to break it with tricks, then you're going to break Cocoa because Cocoa uses it. There are some tricks (like diciu posted) that will make it possible to debug in situations where you suspect nil messaging but just can't seem to find it. But you can't just leave those in your code (and the blog post above makes that clear). nil messaging is just too common inside the frameworks.

To your original point, though, compare:

- (void)doSomethingWith:(id)x {
    NSAssert(x != nil, @"Don't pass me nil");
    [x something];
}

vs.

void Bar::DoSomething(Foo *x) {
    assert(x != NULL);
    if (x != NULL) {
       x.something;
    }
}

In both cases, you need to test, and in both cases the compiler won't warn you if you fail to test. The only difference is in what cases you crash/assert. Personally, I write macros around NSAssert() that make it always print a log message if it fails. It just only crashes in Debug. That way when a customer sends me logs, I can see what assertions failed.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Thanks Rob, I merely included the hard assert for brevity - I'm not a fan of release-build assert crashes. I think the whole send-message-to-nil thing is something I'll just get used to. Still, its worth asking and learning a bit more about what's going on under the hood - everybody wins! – Justicle May 27 '09 at 23:56