I want to show alert whenever an exception occured anywhere in my code.
Without enclosing the code that may throw exception.
Everytime when exception occures it shows error in main class. Is that possible to handle from there?
This is normal way of throwing an exception im using:
#try {
// code that may throw exception
}
#catch (NSException * e) {
// show alert
}
#finally {
}
However, I have found an effective work-around - creating my own exception handler (which is also useful for other reasons). First, create a function that will handle the error and output it to the console (as well as whatever else you want to do with it)
void uncaughtExceptionHandler(NSException *exception) {
NSLog(#"CRASH: %#", exception);
NSLog(#"Stack Trace: %#", [exception callStackSymbols]);
// Internal error reporting
}
Next, add the exception handler to your app delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
// Normal launch stuff
}
If this doesn't work, then there are only two possible reasons:
Something is overwriting your NSSetUncaughtExceptionHandler call (there can be only one handler for your entire app). For example, some 3rd party libraries set their own uncaughtExceptionHandler. So, try setting it at the END of your didFinishLaunchingWithOptions function (or selectively disabling 3rd party libraries). Or better yet, set a symbolic break point on NSSetUncaughtExceptionHandler to quickly see who is calling it. What you may want to do is to modify your current one rather than adding another one.
You're not actually encountering an exception (for example, EXC_BAD_ACCESS is not an exception)
Navigate to Breakpoint panel in Xcode, click "plus" button at the left-bottom to add a new breakpoint, then do this:
build and run your project, Xcode will brings you to the line where exception throws.
If I'm understanding the question correctly, it seems that you're looking for a good way to handle errors in your code that may have been driven by a user action (e.g. invalid data, missing fields in a form, etc.).
The best way to do this would be to use NSError instead of NSException. NSError is generally used to pass information between different code interfaces that are expecting a possible error, where as NSException is more so for errors that are unexpected, or caused by the developer and thus, should be handled during development.
To present an alert from anywhere in your code, you could add a method to your App Delegate and use target-action to pass along your error. That might look like this:
#protocol DLErrorPresentation <NSObject>
- (void)showAlertWithError:(NSError*)error sender:(id)sender
#end
- (void)showAlertWithError:(NSError*)error sender:(id)sender {
// Your alert presentation code
}
// In some view controller or view:
NSError* error;
if(![self somethingThatMayError:error]) {
id<DLErrorPresentation> responder = [self targetForAction:#selector(showAlertWithError:sender:);
[responder showAlertWithError:error sender:self]
}
To provide additional context, here's what Apple says about exceptions in their documentation:
You should reserve the use of exceptions for programming or unexpected runtime errors such as out-of-bounds collection access, attempts to mutate immutable objects, sending an invalid message, and losing the connection to the window server. You usually take care of these sorts of errors with exceptions when an application is being created rather than at runtime.
Instead of exceptions, error objects (NSError) and the Cocoa error-delivery mechanism are the recommended way to communicate expected errors in Cocoa applications.
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Exceptions/Exceptions.html#//apple_ref/doc/uid/10000012i
Although exceptions are commonly used in many programming environments to control programming flow or to signify errors, do not use exceptions in this way in Cocoa and Cocoa Touch applications. Instead, you should use the return value of a method or function to indicate that an error has occurred, and provide information about the problem in an error object.
https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/ExceptionHandling.html#//apple_ref/doc/uid/TP40008195-CH18-SW1
I have added exception handling code inside my main.m.
int main(int argc, char * argv[]) {
#try {
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFrom
Class([AppDelegate class]));
}
} #catch (NSException *exception) {
NSLog(#"exception");
}
}
Its working now.
Related
When i call this function few times from my button (like mad more then few times), then my app crash instead of just throw exception and ignore.
How do you ignore to crash when exception happened?
where localView is UIView *localView.
- (void)smallLocalView {
#try {
self.localView.backgroundColor = [UIColor blackColor];
[self.localView.layer removeFromSuperlayer];
//self.localView.opaque = NO;
}
#catch(NSException *exp) {
//NSLog(#">>> failed %#" , exp.reason);
NSLog(#"OK - but dont crash!!!");
}
#finally {
NSLog(#"OK - but dont crash!!!");
}
}
EDIT:
tl;dr In general you shouldn't try to catch exceptions, you should try to fix the root cause.
There are two kinds of exception you are interested in. The first kind as shown in your image is a system exception. The processor has encountered a problem, for example trying to access protected memory (usually called by trying to dereference a null pointer). You cannot catch that kind of an exception. The operating system will terminate your program when one is raised.
The problem, by the way, is almost certainly caused by localView having been deallocated prematurely, or maybe you access it not on the main thread (as per Edgar's answer).
The other kind of exception is the Objective-C exception which is always raised programmatically. Although you can catch these with a #try { ... } #catch { ... } block, in general you should not. The reason is that there is no enforcement to make code exception safe, so the exception may unwind stack frames where resources need to be deallocated or stack frames where clean up is needed to keep data structures consistent. Once an exception is thrown and caught, you cannot guarantee the logical consistency of your program's state. Your only real option is to terminate as cleanly as possible.
Make sure you are on the Main thread when changing the background color.
You can add an assert at the beginning, like this, other check the stack trace on the left side when you get the exception in order to check in which thread you're on:
NSAssert([NSThread isMainThread]);
Also, depending on where you are calling smallLocalView from, it might happen that your view is not loaded/ready.
- (void)smallLocalView {
if (!self.isViewLoaded) {
return;
}
...
}
I'm running into a situation in iOS (and OS X) where exceptions from NSKeyedUnarchiver cause binary-only third-party frameworks to crash my app (when deployed in the field) when they try to unarchive a corrupt archive, thus forcing users to delete and reinstall the app. This doesn't happen often, but I'd like that number to be zero.
I can't solve the problem by wrapping the NSKeyedUnarchiver calls, both because I don't have the source code and because those calls are not the direct result of anything that my code does; they run on arbitrary background threads at arbitrary times.
I'm currently swizzling the NSKeyedUnarchiver class so that reading a corrupt archive returns nil (as though the file were not there) rather than throwing an exception, but I can't be certain whether any of those third-party frameworks might do things correctly (with an #try/#catch block) and might break in interesting ways if I do so.
It would be helpful if I could somehow examine the Objective-C exception handling tree (or equivalent) to determine whether an exception handler would catch an exception if thrown, and if so, which handler. That way, my patched method could return nil if the exception would make it all the way up to Crashlytics (which would rethrow it, causing a crash), but could rethrow the exception if some other handler would catch it.
Is such a thing possible, and if so, how?
Why not wrap your exception-throwing callsite in a try/catch/finally?
#try {
//call to your third party unarchiver
}
#catch {
//remove your corrupted archive
}
#finally {
//party
}
Rolling your own global exception handler may also be of use here, ala: How do you implement global iPhone Exception Handling?
If you're not sure that wrapping third-party library code with #try/#catch is good enough you can hook NSKeyedUnarchiver methods to replace them with exact same wrapper thus making sure that exception is never gets thrown outside. Here is pseudo-code:
#try {
//call original NSKeyedUnarchiver implementation
}
#catch {
return nil;
}
Objc runtime has public APIs that can do such a thing
I have tried two methods for catching the exceptions. First one is with a try catch and the second one is with the following code in Appdelegate.
void onUncaughtException(NSException* exception)
{
//save exception details
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSSetUncaughtExceptionHandler(&onUncaughtException);
}
The advantage of the second method is we don need to implement try catch blocks in each and every method.
The first one catches the exception, prints it but does not crash the application.. But the second one catches the exception and crashes the application. Is there any way to use the second method to catch exceptions without crashing the application.
NSSetUncaughtExceptionHandler Sets the top-level error-handling function where you can perform last-minute logging before the program terminates. in the onUncaughtException you can do something before crash, but app do crash finally.
#try...#catch...#finally.. is to try to catch possible NSException, if catch, run in the #catch block code, no matter if catch, code will run in #finally block code. Using #try...#catch... will not cause crash finally, this may be the main difference.
how to soive this to stop warning message for this? am trying to put error on label. Does try catch really prevent crashing app?
#catch (NSException *ex) {
errorLbl.text =ex;
}
Instead of trying to catch a crash, you should make sure that code will not crash altogether. However, you can always convert the NSException to NSString
#catch (NSException *ex) {
errorLbl.text = [NSString stringWithFormat:#"%#",[ex reason]];
}
NSException
#interface NSException : NSObject <NSCopying, NSCoding> {
#private
NSString *name;
NSString *reason;
NSDictionary *userInfo;
id reserved;
}
This question is hard to understand, but if your asking, will that Catch catch every exception that is based off of a NSException, then the answer is yes, with a small issue.
You can catch it, but since your not doing anything about it, the code will continue after the catch. If your app is crashing, then what will happen is you will fill some label with the error, but it wont mean the app is in a stable position, it might just keep crashing.
NSException's reason contains a "human readable" reason that you could display, like:
#catch (NSException *ex) {
errorLbl.text = ex.reason;
}
See the NSException reference for more info.
It's worth noting that exceptions in Objective-C (unlike in other languages) are intended to be used for programming or unexpected runtime errors, not normal program flow. The docs state:
Important: You should reserve the use of exceptions for programming or
unexpected runtime errors such as out-of-bounds collection access,
attempts to mutate immutable objects, sending an invalid message, and
losing the connection to the window server. You usually take care of
these sorts of errors with exceptions when an application is being
created rather than at runtime.
...
Instead of exceptions, error
objects (NSError) and the Cocoa error-delivery mechanism are the
recommended way to communicate expected errors in Cocoa applications.
For further information, see Error Handling Programming Guide.
See the Error Handling Programming Guide.
I have a problem with Xcode 4.2 debugging in an iOS 5 simulator/device. The following code crashes, as expected:
NSArray *arr=[NSArray array];
[arr objectAtIndex:100];
In iOS 4, I get a useful stack trace of hex numbers. But in iOS 5, it just gives me:
*** First throw call stack:
(0x16b4052 0x1845d0a 0x16a0674 0x294c 0x6f89d6 0x6f98a6 0x708743 0x7091f8 0x7fcaa9 0x2257fa9 0x16881c5 0x15ed022 0x15eb90a 0x15eadb4 0x15eaccb 0x6f02a7 0x6faa93 0x2889 0x2805)
Thanks.
Nothing I tried would fix this (tried both compilers, both debuggers, etc.)
After upgrading XCode for the iOS 5 update, no stack traces seemed to work.
However, I have found an effective work-around - creating my own exception handler (which is also useful for other reasons). First, create a function that will handle the error and output it to the console (as well as whatever else you want to do with it):
void uncaughtExceptionHandler(NSException *exception) {
NSLog(#"CRASH: %#", exception);
NSLog(#"Stack Trace: %#", [exception callStackSymbols]);
// Internal error reporting
}
Next, add the exception handler to your app delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
// Normal launch stuff
}
That's it!
If this doesn't work, then there are only two possible reasons:
Something is overwriting your NSSetUncaughtExceptionHandler call (there can be only one handler for your entire app). For example, some 3rd party libraries set their own uncaughtExceptionHandler. So, try setting it at the END of your didFinishLaunchingWithOptions function (or selectively disabling 3rd party libraries). Or better yet, set a symbolic break point on NSSetUncaughtExceptionHandler to quickly see who is calling it. What you may want to do is to modify your current one rather than adding another one.
You're not actually encountering an exception (for example, EXC_BAD_ACCESS is not an exception; credit to #Erik B's comments, below)
There is a useful option of adding an Exception Breakpoint (using the + at the bottom of the Breakpoint Navigator). This will break on any Exception (or you can set conditions). I don't know if this choice is new in 4.2 or if I only finally noticed it trying to workaround the missing symbols problem.
Once you hit this breakpoint you can use the Debug Navigator to navigate the call stack, examine variables, etc as usual.
If you do want a symbolicated call stack suitable for copy/pasting or the like, gdb backtrace will work fine from there:
(gdb) bt
#0 0x01f84cf0 in objc_exception_throw ()
#1 0x019efced in -[NSObject doesNotRecognizeSelector:] ()
(etc)
There is a new feature on the debugger. You can set a break point whenever a exception is thrown and stop the execution right there, just as it used to happen on 4.0.
On the "Breakpoint Navigator", add a "Exception Breakpoint" and just press "Done" on the options popup.
That's all!
PS: In some cases would be better to break only for Objective-C exceptions.
Here is one more solution, not so elegant as previous, but if you didn't add exception breakpoints or handlers, it can be only one way to go.
When app crashes, and you get your raw first throw call stack (in hex numbers), type into Xcode console info line *hex (don't forget star and 0x hex specifier), for example:
(gdb) info line *0x2658
Line 15 of "path/to/file/main.m" starts at address 0x25f2 <main+50>
and ends at 0x267e <main+190>.
If you are using lldb, you can type image lookup -a hex (without star in this situation), and you get similar output.
With this method, you can traverse from top of the throw stack (there will be about 5-7 system exception propagators) to your function which caused a crash, and determine exact file and line of code.
Also, for similar effect you can use atos utility in terminal, just type:
atos -o path/to/AplicationBundle.app/Executable 0xAdress1 0xAdress2 0xAdress3 ...
and you get symbolicated stack trace (at least for functions you have debug symbols).
This method is more preferable, because you don't have for each adress call info line, just copy adresses from console output and paste them into terminal.
You can add an Exception Breakpoint (using the + at the bottom of the Breakpoint Navigator) and add the action bt to it (click the Add Action button, select Debugger Command, enter "bt" in the text field). This will display the stack trace as soon as an exception is thrown.
This is a common problem, not getting stack traces in 4.2. You can try swapping between LLDB and GDB to see if you get better results.
File a bug report here.
http://developer.apple.com/bugreporter/
EDIT:
I believe that if you swap back to LLVM GCC 4.2 you'll not see this happen. You may lose features you need though.
Use this code in your main function:
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal;
#try {
retVal = UIApplicationMain(argc, argv, nil, nil);
}
#catch (NSException *exception) {
NSLog(#"CRASH: %#", exception);
NSLog(#"Stack Trace: %#", [exception callStackSymbols]);
}
#finally {
[pool release];
}
return retVal;
}
At Xcode's debug console prompt type:
image lookup -a 0x1234
And it will show you something like:
Address: MyApp[0x00018eb0] (MyApp.__TEXT.__text + 91088)
Summary: MyApp`-[MyViewController viewDidAppear:] + 192 at MyViewController.m:202
Turning 'Compile for Thumb' back on (debug configuration) worked for me.