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/
Related
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.
I've been following along with some tutorials and have built an app that I'd like to publish, but I'm unsure how to deal with error handling.
Tutorials teach you about things like do-catch blocks but whenever they deal with the case where an error is thrown, they just log the error to console, which is obviously unhelpful in a production environment.
Moreover, some examples of the do-catch blocks appear to have little use. As an example, I'm using Realm Database as a core part of my app, and I'm told the way to initialise it is like so in the AppDelegate:
do {
_ = try Realm()
} catch {
// TODO: - Handle Exception
}
But in this case, since realm is a core part of my app, I wouldn't even mind if it crashed the app, because if it doesn't init properly it's going to crash later anyway.
There are obvious cases when I should just display an error to the user (e.g. If you're trying to connect to an API but there is no wifi), or where a default value could be provided, but I don't know what to do here.
There's no single good approach to handling errors, since how an error should be handled completely depends on the exact situation.
In general, however, there are some guidelines on error handling:
Use do-catch blocks on throwable functions unless you have a very
good reason to do so
Avoid runtime errors at all costs and don't throw fatal errors. Even if your app encounters an error that cannot be properly handled (the results of a throwable function is absolutely needed for your app to function), don't let your app crash due to a runtime error, rather let the user know that there's something wrong. A crashing application sends a very bad message to your users, while failing gracefully after encountering an error doesn't seem as bad.
In a catch block, try to solve the error without
letting the user know that there was an error if the error is
recoverable (for instance, you can use a default value instead of the
value you expected from the throwable function)
If the error can't be
handled, let the user know that there was an error (such as they
don't have internet connection when making a network request or a
network request failed for any other reason) and try to give them an
alternative approach (i.e. try later when you have internet
connection)
Only use try! and similar forced methods (force unwrapping, etc.) if you are absolutely sure that the function won't actually throw an error (for instance Realm.init() can only throw an error on the very first call to it in an app's lifecycle, so after the first instantiation of Realm, you can safely do try! Realm()) or if the error represent a programmer error (such as a file not being in the right place, which is needed for the application), but make sure you actually correct such errors in the development phase
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
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.
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.