157

I have to ask this, because: The only thing I recognize is, that if the assertion fails, the app crashes. Is that the reason why to use NSAssert? Or what else is the benefit of it? And is it right to put an NSAssert just above any assumption I make in code, like a function that should never receive a -1 as param but may a -0.9 or -1.1?

TheNeil
  • 3,321
  • 2
  • 27
  • 52

10 Answers10

300

Assert is to make sure a value is what its supposed to be. If an assertion fails that means something went wrong and so the app quits. One reason to use assert would be if you have some function that will not behave or will create very bad side effects if one of the parameters passed to it is not exactly some value (or a range of values) you can put an assert to make sure that value is what you expect it to be, and if it's not then something is really wrong, and so the app quits. Assert can be very useful for debugging/unit testing, and also when you provide frameworks to stop the users from doing "evil" things.

Daniel
  • 22,363
  • 9
  • 64
  • 71
  • 3
    perfect. gonna vote this up tomorrow. reached daily vote limit. Is it ok to keep assert around in a release product? Or must I take these off from my code when building for release? –  Sep 03 '09 at 20:49
  • 9
    You should take out NSAssert for release. There's a compile-time flag to do that. – Barry Wark Sep 03 '09 at 20:51
  • 128
    > You should take out NSAssert for release. This is debatable. I always release my applications with assertions enabled, and this is standard practice for a lot of software, Apple for example does this. As soon as your program has detected an abnormal state you should crash. You can get a stack trace of where the error occurred, whereas if you disable asserts you could end up corrupting memory and/or user data and the problem will be very hard to debug. – Mike Weller Apr 28 '10 at 08:12
  • 1
    I agree with Mike. Generally when I use NSAssert in production code, it's because something very bad will happen if execution continues in that state. IMO If you're using it for debugging recoverable errors, you're using it wrong. In that case what you probably want is to return and handle the particular error. YMMV. – DougW Jan 20 '11 at 18:40
  • 18
    Note that XCode 4 has NS_BLOCK_ASSERTIONS defined by default in release configurations. I guess if you don't change that your released code will not contain NSAssert:s. – Jonny Nov 29 '11 at 02:52
  • 1
    No need to remove from runtime - Apple lets you install runtime assertion handlers (e.g. I use it to automatically log asserts to online analytics - a LOT more useful than random crash reports that users may or may not upload to itunes). c.f. http://red-glasses.com/index.php/tutorials/handling-crashes-and-nsassert-gracefully-in-a-production-app/ – Adam Apr 15 '12 at 23:09
  • 16
    If I understand correctly, what is the point of leaving them (on the release version)? Why not replace the NSAssert with a if statement, and if (something terrible happens), then inform the user (or do something that is under *your* control), and not just quit/crash and leave the user wondering what happened... Or am I missing something? – Gik Nov 27 '12 at 15:34
  • 11
    It is a waste of developer time to go down the path of every exceptional case which is not supposed to happen at all under normal circumstances. This involves thinking of appropriate ways to inform the user for each of them and/or making the app robust enough to carry on in an expected manner after their occurrence. The more practical approach is to crash the app and fix the bug found from the crash report and release a new version. Having said that, it is important to make sure that there is no data loss in any such situation. This has to be ensured nevertheless but it is far lesser work. – trss May 30 '13 at 19:16
  • 1
    I think its good to 'fail early'. It helps debugging enormously. I use a lot of NSAsserts in my viewDidLoad to verify i made the right connections in storyboards for ipad and iphone, for example. – Rob van der Veer Jul 20 '13 at 18:19
  • @MikeWeller, I thought [NSAssert would not be compiled into code at release time](http://stackoverflow.com/a/5010188/1803879) : ). – Tom Howard Oct 22 '15 at 21:07
  • Late as always; the argument against asserts in release builds is that they have a processing cost. So one school of thought uses asserts to validate parameters passed to a function in order to aid in development, using tests that cost whatever they cost, even if it changes big-O complexity (such as checking everything in an array matches a condition rather than merely putting the array somewhere for later indexing). That creates asserts that one would never desire to include in a production build. – Tommy Sep 13 '16 at 13:14
  • 2
    Your app should *never* crash due to an unexpected data. If you can be bothered to write an assertion, you can write an if statement to handle the case gracefully. I'm not sure about Apple application code, I've never seen any. SDK code is different - its appropriate that they Assert there, as the SDK cannot predict how the developer would want to handle a case in their application. As a developer, any implementation can be designed to safely fail over, and should be. – BricoleurDev Feb 27 '17 at 16:14
  • You can argue that every case should be able to handled, but if you write assertions well you'll come up with 10x (or 20x, or more) the cases, none of which should ever happen. If it can happen, by definition it shouldn't be an assertion. It's often better to crash than to continue, too, as your handling of impossible cases could lead to a "mode" the user can't escape. Or, even worse, a quiet form of data corruption that gets written out to disk. And then that's that. – Steven Fisher Apr 29 '17 at 00:57
20

I can't really speak to NSAssert, but I imagine that it works similarly to C's assert().

assert() is used to enforce a semantic contract in your code. What does that mean, you ask?

Well, it's like you said: if you have a function that should never receive a -1, you can have assert() enforce that:

void gimme_positive_ints(int i) {
  assert(i > 0);
}

And now you'll see something like this in the error log (or STDERR):

Assertion i > 0 failed: file example.c, line 2

So not only does it safe-guard against potentially bad inputs but it logs them in a useful, standard way.

Oh, and at least in C assert() was a macro, so you could redefine assert() as a no-op in your release code. I don't know if that's the case with NSAssert (or even assert() any more), but it was pretty useful to compile out those checks.

jotik
  • 17,044
  • 13
  • 58
  • 123
Mando Escamilla
  • 1,560
  • 1
  • 10
  • 17
18

NSAssert gives you more than just crashing the app. It tells you the class, method, and the line where the assertion occurred. All the assertions can also be easily deactivated using NS_BLOCK_ASSERTIONS. Thus making it more suitable for debugging. On the other hand, throwing an NSException only crashes the app. It also does not tell about the location of the exception, nor can it be disabled so simply. See the difference in the images below.

The app crashes because an assertion also raises an exception, as the NSAssert documentation states:

When invoked, an assertion handler prints an error message that includes the method and class names (or the function name). It then raises an NSInternalInconsistencyException exception.

NSAssert:

Logs after an assertion

NSException:

Logs after an exception

Abdurrahman Mubeen Ali
  • 1,331
  • 1
  • 13
  • 19
  • An `NSException` provides plenty of opportunity to customize the output it returns via the `reason` and `userInfo` parameters. There's no reason you couldn't add class name, selector, line information and anything else you care to add to assist with debugging. IMHO, you use an `NSAssert` for debugging purposes during development but disable them to ship; you throw an `NSException` if you want to leave in an assertion in shipping code. – markeissler Aug 27 '15 at 00:30
17

Apart from what everyone said above, the default behaviour of NSAssert() (unlike C’s assert()) is to throw an exception, which you can catch and handle. For instance, Xcode does this.

Jens Ayton
  • 14,532
  • 3
  • 33
  • 47
  • Is there more about how we can catch and handle the exception? – Gon Dec 24 '13 at 11:07
  • 1
    Exceptions in cocoa are NOT "catchable and handleable" de facto. If control passes through an apple function at all anywhere in the call tree, behavior is undefined. Exceptions are purely for error reporting (aka, crittercism, etc), not for general use like in Java. – Michael Jan 02 '14 at 17:20
9

Just to clarify, as somebody mentioned but not fully explained, the reason for having and using asserts instead of just creating custom code (doing ifs and raising an exception for bad data, for instance) is that asserts SHOULD be disabled for production applications.

While developing and debugging, asserts are enabled for you to catch errors. The program will halt when an assert is evaluated as false. But, when compiling for production, the compiler omits the assertion code and actually MAKE YOUR PROGRAM RUN FASTER. By then, hopefully, you have fixed all the bugs. In case your program still has bugs while in production (when assertions are disabled and the program "skips over" the assertions), your program will probably end up crashing at some other point.

From NSAssert's help: "Assertions are disabled if the preprocessor macro NS_BLOCK_ASSERTIONS is defined." So, just put the macro in your distribution target [only].

Ohad Kravchick
  • 1,154
  • 11
  • 15
6

NSAssert (and its stdlib equivalent assert) are to detect programming errors during development. You should never have an assertion that fails in a production (released) application. So you might assert that you never pass a negative number to a method that requires a positive argument. If the assertion ever fails during testing, you have a bug. If, however, the value that's passed is entered by the user, you need to do proper validation of the input rather than relying on the assertion in production (you can set a #define for release builds that disables NSAssert*.

Barry Wark
  • 107,306
  • 24
  • 181
  • 206
  • 2
    +1 because your answer makes the most sense to me! Using NSAssert makes more sense if its for development use, not after release. A user entering a value that is not allowed should be followed by a UI error, not NSAssert crashing the application. The fog has cleared! – pnizzle Jul 01 '13 at 02:28
3

Assertions are commonly used to enforce the intended use of a particular method or piece of logic. Let's say you were writing a method which calculates the sum of two greater than zero integers. In order to make sure the method was always used as intended you would probably put an assert which tests that condition.

Short answer: They enforce that your code is used only as intended.

Lounges
  • 4,674
  • 5
  • 32
  • 43
3

It's worthwhile to point out that aside from run time checking, assert programming is a important facility used when you design your code by contract.

More info on the subject of assertion and design by contract can be found below:

Assertion (software development)

Design by contract

Programming With Assertions

Design by Contract, by Example [Paperback]

Baby Groot
  • 4,637
  • 39
  • 52
  • 71
Boon
  • 40,656
  • 60
  • 209
  • 315
2

To fully answer his question, the point of any type of assert is to aid debugging. It is more valuable to catch errors at their source, then to catch them in the debugger when they cause crashes.

For example, you may pass a value to a function expects values in a certain range. The function may store the value for later use, and on later use the application crashes. The call stack seen in this scenario would not show the source of the bad value. It's better to catch the bad value as it comes in to find out who's passing the bad value and why.

Robert Hawkey
  • 761
  • 7
  • 12
-3

NSAssert make app crash when it match with the condition. If not match with the condition the next statements will execute. Look for the EX below:

I just create an app to test what is the task of NSAssert is:

    - (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self testingFunction:2];
}

-(void)testingFunction: (int)anNum{
    // if anNum < 2 -> the app will crash
    // and the NSLog statement will not execute
    // that mean you cannot see the string: "This statement will execute when anNum < 2"
    // into the log console window of Xcode
    NSAssert(anNum >= 2, @"number you enter less than 2");
    // If anNum >= 2 -> the app will not crash and the below 
    // statement will execute
    NSLog(@"This statement will execute when anNum < 2");
}

into my code the app will not crash.And the test case is:

  • anNum >= 2 -> The app will not crash and you can see the log string:"This statement will execute when anNum < 2" into the outPut log console window
  • anNum < 2 -> The app will crash and you can not see the log string:"This statement will execute when anNum < 2"
  • 1
    You've got it the other way round. "NSAssert make app crash when it match with the condition. If not match with the condition the next statements will execute". NSAssert crashes the app if it does NOT match the condition, and execute normally if it DOES match the condition. – Joe Tam Apr 16 '15 at 18:16
  • App crashes and log message when it doesn't meet the condition otherwise it executes further. – Pravin S. May 25 '17 at 06:19