handling ObjC exceptions in monotouch - ios

Sometimes I'm getting exception from inside objective-c code. in my example these exceptions are not critical and I want the app to keep working.
The question is how do I handle these exceptions?
for example, my app crashes time to time while I'm using GeoCoder class. I don't really care if geocoder failed to geocode location and would like to keep my app alive. including geocoder calls in try-catch blocks doesn't solve the problem.
Any help will be appreciated!

MonoTouch tries to a certain extent to convert ObjC exceptions into managed exceptions. This is done by adding an unhandled exception handler for ObjC exceptions, and in there throw a managed exception. This works on secondary threads, but not on the main thread, because iOS has a try-catch handler in its main run loop, which will kill your app if any exceptions reaches it (in other words the exception is handled, so it will never reach MonoTouch' unhandled exception handler).
The best way is to avoid the ObjC exceptions in the first place (Apple's documentation states that ObjC exceptions should only be used for truly exceptional circumstances, since they have a number of problems - memory leaks is quite common for instance).
It is obviously not possible to avoid all ObjC exceptions, so we try to find workarounds for those cases where it still happens. The best way to get this done for your particular case is for you to create a complete test case and open a bug report here: http://bugzilla.xamarin.com, attaching the test case.

Related

Uncaught exception handler on iOS for particular threads

For metric collection threads, we want them to never crash the app. For crashes from those threads, we want catch them and report to the server without crashing the app.
On Android or Java based app, we can set uncaught exception handler for a particular thread or a thread group. See the following example.
Is there similar mechanism on iOS platform for Objective runtime level uncaught exceptions? Is there any example code as reference?
val pipelineThread = Thread({
try {
intArrayOf(1, 2, 3, 4, 5).asList()
.parallelStream()
.map { e ->
e + 1
}.map {
throw RuntimeException("Pipeline map error.")
}.collect(Collectors.toList())
} catch (e: RuntimeException) {
// Exceptions thrown from pipeline will be handled in the same way
// as regular statements, even if it is parallel stream.
throw StreamPipelineException("Java parallel stream.", e)
}
}, "PipelineThread")
with(pipelineThread) {
setUncaughtExceptionHandler { p0, p1 ->
assert(p0.name.contentEquals("PipelineThread"))
assert(p1 is StreamPipelineException)
assert(p1.cause is RuntimeException)
assert(p1.message.contentEquals("Java parallel stream."))
}
}
I only find the top-level handler setting method.
For metric collection threads, we want them to never crash the app.
On Mac, you should move those threads into a separate process. This is how web browsers ensure that bugs impacting one tab do not impact other tabs or crash the whole browser. Usually the best tool for communicating between those processes is XPC.
On iOS, you will need to write your metric collection code carefully to avoid mistakes and not crash. You cannot spawn other processes directly in iOS today. (I keep hoping some day it will be possible. The OS can handle it. Third-party apps just aren't allowed to.)
NSSetUncaughtExceptionHandler is not intended to prevent crashes. It just gives a mechanism to record a little data or clean up certain things before crashing. Even in those cases, it must be used extremely carefully, since the system is in an undefined state during an uncaught exception. While in principle it's possible to write exception-safe Objective-C, the core frameworks are generally not. After raising an exception, there is no promise what state the system is in, and preconditions may be violated. As the docs note:
The Cocoa frameworks are generally not exception-safe. The general pattern is that exceptions are reserved for programmer error only, and the program catching such an exception should quit soon afterwards.
Assuming you're using NSThread or NSOperation for your "metric collection threads," you can create something similar to Java's approach using this, but I don't recommend it:
- (void)main {
#try {
... your code ...
} #catch ( NSException *e ) {
// Handle the exception and end the thread/operation
}
}
I don't recommend this because it can create difficult-to-solve bugs in distant parts of the program because the frameworks are not exception-safe. But I've seen it in enough production code to know it often works well enough. You have to decide if "app crashes and user relaunches it" is worse than "app doesn't crash but now behaves kind of weird and maybe corrupts some user data, but maybe doesn't."
But many Objective-C crashes are not due to exceptions in any case. They are due to signals, most commonly because of memory violations. In this case, the system is in an even less stable state. Even allocating or releasing memory may be invalid. (I once made the mistake of allocating memory in a signal handler, and deadlocked the program in a tight busy-loop. The Mac actually got hot. It would have been better to have just crashed.) For more, see How to prevent EXC_BAD_ACCESS from crashing an app?
Java incurs a lot of overhead, including a full virtual machine and extra bounds checks, to make it possible to catch the vast majority of error conditions (even ones you can't really recover from). It's a trade-off. It's still possible to have Java code that is not exception-safe and can fail to work correctly once an exception has been thrown, and it sometimes would be nice if Java would just crash and show you where the error is rather than programmers quietly swallowing the errors... but that's a different rant. Sometimes the sandbox makes things very nice.
Objective-C is built directly on C and allows accessing raw memory. If you do that incorrectly, it's undefined behavior and just about anything can happen, though usually the OS will kill the program. So even though it's possible to catch some kinds of exceptions, you cannot prevent all crashes.

Is there any way to disable all thrown exceptions in iOS market release?

Working on an iOS app. Apart from deciding whether it is right action or not, is there any way to disable all exception (which are thrown) in iOS market release? So that it will act like, all exceptions will be received in a common block which will do nothing.
You can apply a 'catch-all' exception handler using NSSetUncaughtExceptionHandler, which you call by passing a closure, e.g:
NSSetUncaughtExceptionHandler { exception in
print("Uncaught exception: \(exception)")
}
A decent place to put this code would be in application:didFinishLaunchingWithOptions. That being said, it's probably a better idea to protect specific parts of the code which may fail with try/catch blocks in which you can mitigate the problems and prevent the app from getting into undefined states.

Exception breakpoints in xcode for iOS, is exceptions excusable?

Am new to the iOS development. While looking into one of the App I work on, with exception breakpoint enabled in xcode. I see so many repetitive exceptions been raised.
But till now, we never realised this, as iOS was excusing those exceptions. The main exception was happening in our service layer, where, in an XML parsing, one of the element mentioned as key was missing. since so many back end requests are fired, for every XML response, and for multiple items inside it, this exception is thrown.
Now that our app is published, will this impact the device's stability? We are planning to fix thee kind of issues. But now, Will these be logged into Apple's diagnostic information. Just curious to know these. Can someone shed some light over it?
In Objective-C, Exceptions are used to signal fatal errors, that is programmer errors and "unexpected" and unrecoverable runtime errors. In Objective-C, Exceptions are a debugging and testing aid, not a construct to control program flow or to regain control after an error.
Usually, an application in a release state shouldn't throw exceptions. If it happens anyway, the only correct course of action is to terminate the program as soon as possible. In general, the Objective-C system frameworks are not exception safe. This means, after an exception has been thrown the application's state will be corrupt. If your program continues, even worse things may happen.
There are only a few, always undocumented harmless cases where exceptions will be thrown and caught by system libraries without leaving the program in an unstable condition.
See also https://developer.apple.com/library/ios/documentation/cocoa/conceptual/Exceptions/Exceptions.html

What's the deal with catching exceptions?

So I've read a lot about catching exceptions. Let's talk about this and iOS together. I've used it with Google Analytics to submit information about the crash and using that to fix bugs.
But this raises a question. Can catching these exceptions help prevent apps from crashing. Can you theoretically prevent that bit of code from crashing the app and keep the app open. Now I get the fact that it would probably be impossible to do if there was no memory to be used but it would still be nice to know about.
Sorry if this sounds like a stupid questions and I really should read more about it and do some more research. Any information would be helpful.
I do have a fairly decent knowledge of iOS obj-c for my age and am willing to look into what you have to say.
Thanks!
Exceptions on iOS should never be caught; they are fatal for a reason. Unlike most languages that have a rich exception hierarchy and multiple means of throwing/catching exceptions for the benefit of the program as a whole, Cocoa-Touch code is built around the principle that all exceptions are fatal. It is a mistake to think that you can catch an exception thrown through any frames of Apple-provided code and have your process continue unhindered. It is an even more grave mistake to catch and rethrow the exception for the purpose of logging.
The exceptions thrown by Cocoa-Touch indicate serious errors in program logic, or undefined and unresolvable state in an object. It is not OK to ignore them, or log them after catching them. They must be fixed and prevented from being thrown in the first place in order to truly guarantee your process remains stable.

iOS: Prevent library from crashing entire app

I've written a library for iOS that is referenced via a single .h file (MyAdaptedViewController.h).
This is essentially a view that sits inside a full UIViewController.
However due to the nature of the library, it may crash (dealing with signal processing, network connectivity and audio input/output).
What I would like would be for the entire app be protected from crashing if a crash occurs in the single UIViewController, i.e. if a crash occured, the user could continue using the app while MyAdaptedViewController was disabled. I understand that this will depend on the type of crash, but I was hoping most crashes / exceptions could be caught?
E.g.
would #try{}#catch{} or
void uncaughtExceptionHandler(NSException *exception)
be possible solutions?
NO! Catching exceptions in an iOS app is a exception to normal Cocoa coding conventions.
NSException is for exceptional errors that you can not be expected to recover from at run-time.
NSError is for errors you are excepted to recover from, or at least show the user, at run-time.
Do not try to handle every error that can be raised by catching exceptions. The APIs that Apple provide will only raise exceptions if you have done a programming error. For example if you try to access an object index out of bounds in an array, this is a programming error since you should have gotten your index correct before accessing the object.
For all other cases you should use instances of NSError, just as Apple do.
If it is proper to do so handle the error in your lib internally. If not possible pass the error up to your caller, and let them handle it.
I have written a longer blog post on the topic here: http://blog.jayway.com/2010/10/13/exceptions-and-errors-on-ios/

Resources