Vincent Gable’s Blog

December 4, 2008

NSAssert Considered Harmful

Filed under: Bug Bite,Cocoa,MacOSX,Objective-C,Programming,Tips | , , , ,
― Vincent Gable on December 4, 2008

The NSAssert() function is unreliable, and therefore dangerous. Plain-old assert() is a better choice.

Note that #define NDEBUG disables assert(); and #define NS_BLOCK_ASSERTIONS disables NSAssert(). One kind of assert being disabled does not mean the other kind is. Nether kind of assertion is disabled by default when building in “Release” mode — you must be sure to disable them yourself. Be careful, a user will not appreciate the difference between a bug and a failed assertion — both are a crash.

assert() immediately stops your program with a SIGABORT signal. You can’t miss a failed assert(), its behavior is predictable, and it fails fast (link is a PDF).

But a failed NSAssert() behaves unpredictably. Sometimes it stops program execution, sometimes it does not and leaves the program in a strange inconsistent state. I have entirely missed NSAssert()s failing, because I did not look at the console. if(!expr) NSLog(); would have been a better choice in the those cases, because at least I would have known how it behaved.

According to Apple,

Assertion macros, such as NSAssert and NSCAssert … pass a string to an NSAssertionHandler object describing the failure. Each thread has its own NSAssertionHandler object. When invoked, an assertion handler prints an error message that includes the method and class (or function) containing the assertion and raises an NSInternalInconsistencyException.

Complicated! The complexity means it is possible to customize what happens when an NSAssert() fails. That sounds cool, but I’ve never heard of someone needing to actually do that.

If a framework catches NSInternalInconsistencyExceptions, then your program will keep right on running after a failed NSAssert(). I have had this happen to me several times. I apologize for not having taken the time to investigate what frameworks were catching what.

Apple could change what catches what with any software update.

Variability and complexity are the last things you want while debugging. There’s no reason to invite it them by using NSAssert() over assert(). Since NSAssert() is not guaranteed to stop your program, it can not be relied on to guard against data corruption, or anything worse then a predictable crash.

UPDATE 2009-06-01: You can annotate assert(), so it prints an explanation like NSAssert(), by &&ing in a string after the condition. For example assert(i < j) is a lot more useful with an explanation: assert(i < j && "unexpected cycle in graph") — on failure it prints

Assertion failed: (i < j && “unexpected cycle in graph”), function WillHalt(), file /path/to/code.m, line 30.

4 Comments »

  1. I appreciate the point about the NSAssert v assert complexity issue.

    Turning off asserts in release builds is another matter perhaps.
    An assert catches an undesirable internal program state which the application may or may not be able to endure.
    If the occurrence of the undesirable state has not been eliminated during testing then its occurrence in a release build is still problematic.
    If you leave the assert in place then at least the program terminates due to a known test at a known location.
    If the defective state is allowed to propagate then a crash may or may not occur at a later stage.

    Maybe it’s one of those pay your money and take your chance sort of things!

    Comment by Jonathan Mitchell — December 8, 2008 @ 11:50 am

  2. You’re right, turning off asserts is another issue entirely. And I’m going to side-step it by claiming the right answer is to not use asserts in production code.

    That said, i haven’t yet eliminated them from code I wrote before I knew better. For what it’s worth, I leave them enabled when I build that code. The theory is that more predictable crashes are better crashes; and it helps motivate me to re-write that the code that needs it. But I admit I’m not totally sure it’s the right answer; it’s just the lesser of evils I know.

    Comment by Vincent Gable — December 11, 2008 @ 5:51 pm

  3. I totally agree! I just made ‘AGAssert’ which eliminates these problems.

    Comment by Håvard Fossli — May 20, 2013 @ 7:37 am

  4. Here’s the link!
    https://github.com/hfossli/agassert

    Comment by Håvard Fossli — May 20, 2013 @ 7:37 am

RSS feed for comments on this post.

Leave a comment

Powered by WordPress