How to catch exceptions within BlockCode (Objective C) - objective-c

Is there a proper way to catch exceptions within block code?
I got the following code:
void(^callback(int) = ^(int respond){
[self DoSomethingWithRespond:respond]; //this throws an exception
};
-(void)DoSomethingWithRespond:(int)respond{
if(respond == 400){
NSException *exception = [NSException
exceptionWithName:#"Failed"
reason:logMessage
userInfo:nil];
#throw exception
}
}
The callback methods gets called from another thread. If the respond is equal to 400 the DoSomethingWithRespond method will throw an exception.

#try {
<#statements#>
}
#catch (NSException *exception) {
<#handler#>
}
#finally {
<#statements#>
}

Related

#throw not caught by #catch block?

If there is a code which looks like
#try
{
#throw [NSException new];
}
#catch (NSException ex)
{
NSLog(#"exception caught");
}
in this case, the code does not go to #catch block, rather the application crashes. How should we catch exceptions throws by #throw in objective-c
[NSException new] instantiates a null class because it contains no useful information. It does not generate an NSException instance, and as such your:
#catch (NSException *ex)
{
NSLog(#"exception caught");
}
is useless. However, if you use:
#catch (id exception)
{
}
You will catch this empty object.
An excerpt from the official documentation on Handling Exceptions:
You can have a sequence of #catch error-handling blocks. Each block
handles an exception object of a different type. You should order this
sequence of #catch blocks from the most-specific to the least-specific
type of exception object (the least specific type being id) ...
You'll have to initialize the NSException using the
#throw [NSException exceptionWithName:#"Exception!" reason:nil userInfo:nil];
or some other valid way to construct NSException listed in the "Creating and Raising an NSException Object" page in Apple documentation. https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSException_Class/index.html#//apple_ref/occ/cl/NSException

Xcode debugger can't catch exception if finally block returns

I discovered weird behavior of Xcode.
Xcode debugger doesn't break for uncaught exception in this code.
#try { #throw #"AA"; }
#catch (...) { #throw; }
#finally { return; }
But exception in this code caught and trigger Xcode break execution for debugging.
#try { #throw #"AA"; }
#catch (...) { #throw; }
#finally { }
If #finally block returns debugger can't catch the exception. Have you ever seen this problem? I'm not sure this is really an issue. By the perspective it looks like designed behavior. I don't know why. Shouldn't I return in #finally block? My problem is it swallows exception silently, so I can't detect it.
Shame on me, I don't know well try...catch...finally behaviors. I almost haven't used exception catching code. Is this designed behavior or buggy behavior? Is this any known issue?
Here's my environment.
Xcode Version 4.4 (4F250)
OS X 10.7.4
Edit
I attach full test source code.
#import <Foundation/Foundation.h>
int main (int a, char** b)
{
#try
{
NSLog(#"trying something...");
#try { #throw #"AA"; }
#catch (...) { #throw; }
#finally { return 0; }
}
#catch (...)
{
NSLog(#"something catched.");
}
#finally
{
NSLog(#"finally...");
}
}
Putting a return in a #finally block seems like a bad idea. The exception handling mechanism is going to try to unwind the call stack as it deals with the exception you're throwing. If a #finally block changes what's on the stack, you undermine the exception handler. It doesn't seem at all surprising that this crashes.
Also, as bbum pointed out, exceptions aren't used for flow control in Cocoa and Cocoa Touch. Throwing an exception through a Cocoa method usually fails. Even if what you're doing is supposed to work in generic Objective-C, it would probably still cause problems in real code.
Short answer: Don't do that.

Error propagatin inside try/catch in cocoa

I want to raise an exception inside a catch block of a method and handle it in catch of another method from where earlier method was called.
I tried this logic-
method B()
{
#try{
.......
// calling method where is probable chance of exception
method A();
}
catch(NSException e)
{
//catching the exception thrown in the method B()
NSString* theError=[e reason];
NSLog(#"the error is == %#",theError);
}
}
method A()
{
#try{
.............
//throw an exception incase of some condition
throw e;
}
catch(NSException e)
{
//rethrowing the exception, want to catch in the method from where this method is called.
throw e;
}
}
But the catch block of method B() is never accessible.
The control never returns to the catch block of method B().
Please suggest.
Thanks,
Sudhansu
Here is a bit of code. I am calling a method (populateData) of TableController from MyController.
The exception supposed to occur in another method of TableController(initializeTest) and i am throwing it
inside try block of FinderCompleted method. In the catch block of same method , rethrowing the exception, as
I don't want to handle it here.
The control is restricted only to the innermost catch block of method - (void)FinderCompleted:(id)args
the NSLog prints like this-
m here 1
Inside the error
You got nothing in ur bucket :D
Throwing exception for the 1st time
Gotcha--1st time exception , throwing it 2nd time
the error is == Something unexpected happened --EXCEPTION
After that i don't know where the control is going. I want the control to go catch block of the outer method which calls FinderCompleted method,
and print the other logs like-
Gotcha--2st time exception, throwing it 3rd time
Gotcha--3rd time exception
Throwing exception for the 4th time
Gotcha--4th time exception
the error is Something unexpected happened --EXCEPTION
in MyController.m
- (IBAction)fetchResults:(id)sender
{
NSArray *tableColumnArray = ...............;//some values initialized
NSArray *identifierArray = ................;//some values initialized
NSArray *bindVariableArray = ................;//some values initialized
TableController *pTC = [[TableController alloc] init];
#try
{
[pTC populateData :tableColumnArray :identifierArray :bindVariableArray];// calling populate DataForMD method defined in TableController class
[pTC release];
}
#catch (NSException * e)
{
NSLog(#"Gotcha--4th time exception");
//want to handle the exception here
NSString* theError=[e reason];
NSLog(#"the error is %#",theError);
}
}
in TableController.m
-(void)populateData:(NSArray *)tableColumnArray:(NSArray *)identifierArray:(NSArray *)bindVariableArray
{
[self setTableColumnArray:tableColumnArray];
[self setColumnIdentifierArray:identifierArray];
[self setBindVarArray:bindVariableArray];
#try
{
NSLog(#"m here 1");
[self initializeTest];// calling initializeTest method
}
#catch (NSException * e)
{
//Do not want to handle it here
NSLog(#"Gotcha--3rd time exception");
NSLog(#"Throwing exception for the 4th time");
#throw e;
}
}
-(void)initializeTest
{
#try
{
ISTQuery* theQuery = (ISTQuery*)[ISTQueryGenerator getQueryByName:[self queryClassName]];
..........
...........//some loc here
[theQuery run];
.................//some loc here
if(theQuery)
{
//Calling FinderCompleted method
//supposed to get error here
[[self modelFinder] startWithRecipient:self andNotificationSelector:#selector(FinderCompleted:)];
}
}
#catch(NSException *e)
{
NSLog(#"Gotcha--2st time exception, throwing it 3rd time");
//Do not want to handle it here
#throw e; // rethrows e implicitly
}
}
- (void)FinderCompleted:(id)args
{
#try
{ //getting some error while back-end transaction
NSString* theError = [ISTModelFinder errorMessageFromFinderArgs:args];
if (theError)
{
NSLog(#"Inside the error");
NSLog(#"You got nothing in ur bucket :D");
NSException *e = [NSException
exceptionWithName:#"InternalErrorException"
reason:#"Something unexpected happened --EXCEPTION"
userInfo:nil];
NSLog(#"Throwing exception for the 1st time");
#throw e;
}
else
{
//do sth else
}
}
#catch(NSException *e)
{
NSLog(#"Gotcha--1st time exception , throwing it 2nd time");
NSString* theError=[e reason];
//Do not want to handle it here
NSLog(#"the error is == %#",theError);
#throw e; // rethrows e implicitly
}
}
You cannot use exceptions for flow control in Cocoa or iOS programming. Exceptions are purely for identifying unrecoverable errors and, typically, the program crashes purposefully very shortly thereafter. (With a handful of exceptions to this rule, most of which have bugs filed against them to deprecate and eliminate the related API.)
Use the NSError pattern to manage user recoverable errors.
It isn't clear why your code isn't working. But that doesn't look like real code. What have you tried?
I had added the code snippet, please suggest how to achieve that
functionality.
The first thing you do is do not use exceptions.
Use the NSError pattern. You will need to refactor your code to do so. It is worth it.
See the documentation on error handling in cocoa.

How to catch NSInvalidArgumentException?

is there any way to catch it? Or is this a bug?
So to answer this question, I used the following:
#try{
//your code
} #catch (NSException* exception) {
NSLog(#"Got exception: %# Reason: %#", exception.name, exception.reason);
}
and the string NSInvalidArgumentException is printed for 'exception.name' so clearly one could just test that exception.name for the actual exception type.
Not the prettiest, but it works.
If I understand you correctly, you ask why you can't catch the NSInvalidArgumentsException. To be precise, the exception is not caught even if you try to catch it as NSException, which should always work. I also experienced this problem and after looking around - this issue indeed looks like an Apple's bug. Please see this page. People report there that the issue exists only on simulators and works correctly on real devices.
As #David mentioned we can use below code
#try{
//your code
} #catch (NSException* exception) {
NSLog(#"Got exception: %# Reason: %#", exception.name, exception.reason);
}
and #Moshe Kravchik mentioned, there situations were some exceptions are not caught , that is mainly when we dispatches new block in a queue , so I would recommend to add separate try catch block for each queue dispatches.
ie, try..catch in main() will not catch the exception happened in AppDelegate class, so need to add separate try..catch in AppDelegate
eg:
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
#try{
self.viewController = [[MainViewController alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
#try{
NSLog(#"This is a block");
NSObject * b= nil;
[NSArray arrayWithObject:b];
} #catch (NSException* exception) {
NSLog(#"Got exception: %# Reason: %#", exception.name, exception.reason);
}
});
return [super application:application didFinishLaunchingWithOptions:launchOptions];
} #catch (NSException* exception) {
NSLog(#"Got exception: %# Reason: %#", exception.name, exception.reason);
}
}
for any uncaught exception, we should use NSSetUncaughtExceptionHandler, that will not stop application from crashing.
void onUncaughtException(NSException* exception)
{
NSLog(#"onUncaughtException : %#", exception.reason);
}
int main(int argc, char* argv[])
{
#autoreleasepool {
NSSetUncaughtExceptionHandler(&onUncaughtException);
int retVal = UIApplicationMain(argc, argv, nil, #"AppDelegate");
return retVal;
}
}
for signals other than exception , like bad memory usages etc we should listen for particular signal
void signalHandler(int signal)
{
NSLog(#"Got signal %d",signal);
}
signal(SIGPIPE, &signalHandler);
signal(SIGABRT, &signalHandler );
signal(SIGHUP, &signalHandler );
signal(SIGINT, &signalHandler );
signal(SIGQUIT, &signalHandler );
signal(SIGILL, &signalHandler );
signal(SIGIOT, &signalHandler );
signal(SIGFPE, &signalHandler );
signal(SIGSEGV, &signalHandler );
signal(SIGSYS, &signalHandler );
signal(SIGPIPE, &signalHandler );
More details about uncaught exception here.
It is a bug. Specifically, a bug in your code. NSInvalidArgumentException means you've passed bad data into a method. Frequently this means you've passed nil as an argument that doesn't allow nil. The exception description should provide more information as to what method/argument is bad.

access objective-c exception in finally block

Given the following situation:
#try {
#try {
// raises an exception :)
[receiver raisingFirstException];
} #finally {
// raises another exception :)
[otherReceiver raisingFinalException];
}
} #catch (id e) {
printf("exception: %s\n", [[e stringValue] cString]);
}
Is there any way to either get the first exception within the
#finally block or to get both exceptions within the #catch block?
I have code where the #finally block does some checks which may raise an
exception but I don't want to loose the original exception (the root cause).
If there was no original exception but the checks fail I want the
exception they throw.
The best way to do this is to assign the exception to a variable that is accessible from the rest of your block.
NSException *ex;
#try {
#try {
[someObject methodWhichCouldThrowException];
} #catch (NSException *e) {
ex = e;
} #finally {
[anotherObject methodWhichCouldThrowADifferentException];
}
} #catch (NSException *e) {
// From here you can access both the exception thrown by 'someObject'
// as well as the exception thrown by 'anotherObject'.
}