5 ms·
I'm sooo happy to see prominent developers advocating for leaving sanity checks (via precondition) on in released builds! Love the article, but love that recomm
by mattiemass 11y ago
I'm sooo happy to see prominent developers advocating for leaving sanity checks (via precondition) on in released builds! Love the article, but love that recommendation more.
- mikeash 11y agoThe common wisdom of only having them be active in debug builds baffles me. Sometimes the argument is from performance. I can buy that. But the most frequent argument I see is basically, "maybe it'll still work." Basically, it's rude to crash the app on purpose and lose the user's work when maybe, maybe, if you keep on going from an assertion failure the program will still work somehow. Sure. And maybe you'll crash strangely. And maybe you'll corrupt the user's data, ruining all of their work, not just whatever you didn't save. What really drives me nuts is that, on the Mac, Apple codifies this crazy idea. The NSAssert macro throws an exception on failure, and uncaught exceptions on the main thread get caught by a handler in the event loop which logs the exception and continues running the app!
- DrTung 11y agoReminds me of Objective C happily calling a nil ptr, Apple wants to be generous and not pester you by terminating your app :-)
- mikeash 11y agoThat's not really the same. Messaging nil is not necessarily an error. Making it an error is just an arbitrary language choice. Messaging nil in Objective-C isn't an uncaught error, but an explicitly allowed action with fully defined results. One could certainly argue that making nil messaging an error is better, but either way it's a different sort of reasoning.
- melling 11y agoApple didn't invent Objective C. It has been this way since 1983, before Steve Jobs left Apple the first time: https://en.m.wikipedia.org/wiki/Objective-C https://en.m.wikipedia.org/wiki/Objective-C
- mdw 11y agoSwift will happily message nil also, for example: delegate?.someOptionalMessage?(param: param) It's a great language feature I appreciated in Obj-C, and not lost to Swift!
- jcizzle 11y agoYes, but that is just the default behavior, does this not work? NSSetUncaughtExceptionHandler({exception in print("\(exception)") exit(1) });
- mikeash 11y agoNope. These exceptions aren't actually uncaught, they're caught by an exception handler in the event loop. NSSetUncaughtExceptionHandler only catches exceptions thrown when there's no handler in place at all, all the way up to the top of the current thread's stack. In practice, this means it really only works for exceptions thrown on secondary threads. Even if you could modify the behavior, having the default work this way would still be terrible.
- jcizzle 11y agoInteresting. Just tried another approach, and you can get the behavior by overriding reportException: in a custom NSApplication subclass. edit: Also I'm not suggesting to just naively exit(1). edit2: Strangely, exception generating code in [[NSOperationQueue mainQueue] addOperationWithBlock:] does actually hit the NSUncaughtExceptionHandler, which was surprising to me.
- mikeash 11y agoThe mainQueue thing is because the catch-all exception handler is in the event loop, not the runloop. So if you throw during event handling (mouse down, key down) you'll hit it, but if it's something else (GCD, timers) you won't. It's extremely weird. Good call about reportException:. Although I personally prefer to just avoid NSAssert. The good old C assert() gets the job done well for me.
- jcizzle 11y agoGood to note, I must have logically mapped addOperationWithBlock: to a typical NSResponder event in my head, but what you're saying makes more sense.
- bsaul 11y agoBut then, throwing exceptions instead of just crashing the app can also let tools like crashlytics get them and send them to a web service before the app really terminates.
- mikeash 11y agoThrowing an exception isn't really the problem, the problem is the catch-all exception handler in the event loop that just logs them and continues running the app. If that was gone, and NSAssert threw exceptions that by default crashed the app, that would be pretty much fine.
- astrange 11y agoYou can always catch regular crashes in your app through Mach exceptions, or by scanning the crash logs directory for your app.
- _greim_ 11y agoObservation: JavaScript runtime errors don't crash my browser tab, just halt the current call stack. I wonder if that's a consequence of bad/adhoc architecture, or if browsers represent a class of apps where silently swallowing errors is the right thing to do?
- underbluewaters 11y agoIt's probably a historical oddity. Remember, Javascript started out as just an optional piece to copy-and-paste nifty effects in the beginning. You wouldn't want one script tag to kill the whole page. For a web app this behavior is horrible, and I say that as a big fan of Javascript and the web platform in general. I would love to be able to specify a setting and just have exceptions kill the app with a nice error message. So many times have I had users report weird problems in my single-page-app, and my only possible explanation is that they've had the app open in a tab for days on end, run into a random unrelated silent exception at one point, and that has later affected the behavior of a different feature later. Sigh...
- astrange 11y ago> What really drives me nuts is that, on the Mac, Apple codifies this crazy idea. The NSAssert macro throws an exception on failure, and uncaught exceptions on the main thread get caught by a handler in the event loop which logs the exception and continues running the app! There's a defaults setting you can use that will pop up a window with crash/continue options if it catches your NSException. But it is inconsistent - some old stuff like NSInvocation also catches and throws out exceptions, but dispatch queues will kill your process if they catch one.
- kibwen 11y agoThis is also something that Rust encourages. The default `assert!()` macro remains in even in release builds, and you may use the `debug_assert!()` macro to have the assertion present only in debug builds.