I have a method that has several parts that can throw an exception. If one of these parts fail, I would like the cleaning method to run. I am thinking about using the try/catch directive.
My question is: will I have to use one directive for every line of code that can throw an exception or can I simply include the whole method in a block like this?
#try {
[self doStuff];
// doStuff has several passages that could throw an exception
}
#catch (NSException * e) {
[self cleanTheWholeThing];
}
In this case it is not important to me which line generated the problem. I just need the method to run successfully or do other stuff in case it fails.
thanks
If you can, avoid exceptions. Even Apple recommends to avoid them:
Instead of exceptions, error objects (NSError) and the Cocoa error-delivery mechanism are the recommended way to communicate expected errors in Cocoa applications.
See also their Error Handling Programming Guide (it's marked as being Mac related but is equally valid for iPhone, the same concepts apply).
The reasons for avoiding exceptions are that I know of are:
They are slower than reporting via NSError and an out-pointer.
They can result in memory leaks if you aren't very careful. Some memory leaks due to exceptions cannot be avoided at all (in non-GC environments).
You might forget to catch them, and then your app crashes.
In general, exceptions in Objective-C are used for really exceptional problems that are often unrecoverable. They are almost never used in area where you expect something can go wrong (like network communication; the NSURLConnection methods do not throw exceptions for this reason but export an NSError). This may be different from other languages where exceptions are used more often. In the projects I've been working on I had only once the need to catch and handle an exception (can't remember which, though).
Instead, you should do something like this:
// Returns YES when successful.
- (BOOL)doSomething:(NSError **)outError
{
// ...
if (someErrorOccurred) {
if (outError) {
outError = [NSError
errorWithDomain:#"MyErrorDomain"
code:123
userInfo:nil
];
// Or maybe even use your own NSError subclass
return NO;
}
}
// ...
// Operation was successful.
return YES;
}
You can certainly have multiple lines in your try block. Example:
#try {
if (managedObjectContext == nil) {
actionMessage = #"accessing user recipe library";
[self initCoreDataStack];
}
actionMessage = #"finding recipes";
recipes = [self recipesMatchingSearchParameters];
actionMessage = #"generating recipe summaries";
summaries = [self summariesFromRecipes:recipes];
}
#catch (NSException *exception) {
NSMutableDictionary *errorDict = [NSMutableDictionary dictionary];
[errorDict setObject:[NSString stringWithFormat:#"Error %#: %#", actionMessage, [exception reason]] forKey:OSAScriptErrorMessage];
[errorDict setObject:[NSNumber numberWithInt:errOSAGeneralError] forKey:OSAScriptErrorNumber];
*errorInfo = errorDict;
return input;
} #catch (OtherException * e) {
....
} #finally {
// Any clean up can happen here.
// Finally will be called if an exception is thrown or not.
}
And a link to practical use of exceptions:
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Exceptions/Tasks/HandlingExceptions.html
Its completely alright to enclose the method call with try-catch block.
If you don't care which line caused the exception you can enclose the entire function in a try/catch block.
For example, assume that f1(), f2() or f3() can throw an exception in this code:
try {
f1();
f2();
f3();
}
catch( ... ) {
...either f1, f2 or f3 threw an exception - don't know which
}
You can include the whole method body in your try block.
In your catch part you can have multiple catch blocks to handle different types of exceptions:
#catch (NSException * e) {
....
}
#catch (OtherException * e) {
....
}
#finally {
NSLog(#"finally");
}
so you could also discern exactly which line failed based on the specific exception raised, if you ever need it.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Exceptions/Tasks/HandlingExceptions.html
You can include the whole method body in your try block.
Related
i am using the following code to catch an error when ind.row is not a valid value or sometimes it becomes nil
#try {
if(ind.row<[[treeNode flattenElements] count])
{
[self.mTableView scrollToRowAtIndexPath:ind atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
#catch (NSException *e) {
NSLog(#"%#",e);
}
but when this code executes sometimes this error is occuring
Assertion failure in -[NSIndexPath row], /SourceCache/UIKit_Sim/UIKit-1262.60.3/UITableViewSupport.m:1948`
what may be the reason for this error and why exception is not being handled
Assertions in iOS don't throw exceptions, so you can't catch them.
You are going to need to figure out what's wrong with your call to -row. My first guess would be that "ind" is already freed or something.
You'll have to check the index, generate and raise an exception on you own:NSException Class Reference
+ (void)raise:(NSString *)name format:(NSString *)format, ...
actually it was already catching the exceptions...i had set the flag stop on objc exceptions.when i removed that it catching the exceptions.... as pointed out by #stilesCrisis the ind value was null at the time of this exception..sorry for the trble
I'm currently using Facebook C# SDK v4.2.1 and I'm trying to post something onto the user wall. It worked fine until I got an FacebookOAuthException (OAuthException) Error validating access token. error and I can't catch that exception and it crashes my app.
I'm using this call FacebookApp.ApiAsync("/me/feed", ...). Because it happens async I'm not sure where I have to put my try-catch block to catch that error but with no success
This is what I'm using:
private void shareFBButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
// ... code for preparing strings to post ...
try
{
// setup FacebookApp and params ...
app.ApiAsync("/me/feed", args, HttpMethod.Post, (o) => {
if (o.Error != null)
{
Debug.WriteLine("ERROR sharing on Facebook: " + o.Error.Message);
}
else
{
Debug.WriteLine("FB post success!");
}
}, null);
}
catch (Exception ex)
{
Debug.WriteLine("ERROR sharing on Facebook: " + ex.Message);
}
}
So can someone tell me where I have to put my try-catch block, so I can catch the OAuthException?
EDIT:
After further investigation, the FacebookOAuthExcpetion is thrown from Facebook C# SDK after the SDK catches WebException and FacebookApiException. For further information look at "Pavel Surmenok" his answer. That is exactly what is happening.
As of the moment the only solution for catching FacebookApiException (base class of all Facebook SDK exceptions) is to catch it in App.UnhandledException method. Check type of e.ExceptionObject and if it is a FacebookApiException set e.Handled to true and the app won't exit itself anymore.
I found a solution for my problem. Maybe I should rephrase my question.
"How to catch an exception which occurred on a background thread?"
Which is exactly what is happening in my original question. An exception is throw inside the Facebook C# SDK on a background thread because Api calls are executed asynchronously.
Maybe most of you already know this, but I didn't because I'm new to WP7 development.
Solution:
In App.UnhandledException event handler, just set the e.Handled flag to true. Then the app won't exit ifself.
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
// catch Facebook API exceptions
// if handled is set to true, app won't exit
if (e.ExceptionObject is FacebookApiException)
{
e.Handled = true;
// notify user of error ...
return;
}
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
}
Not sure if this is the right way to catch an API exception, but works fine for now.
I've reproduced this trouble. As I can see, the exception is generated in FacebookApp.ResponseCallback method. It contains "try" block with two "catch" sections (one for FacebookApiException and one for WebException). In the end of each "catch" sections the exception is being rethrown and is never been handled (that's why your app crashes). So, the debugger says you about this (rethrown) exception.
Later in "finally" section they create FacebookAsyncResult with reference to this exception in the property "Error".
I think that your solution (to handle this exception in App.UnhandledException) is the most appropriate one.
By the way, it's interesting, why SDK developers decided to rethrow exceptions in FacebookApp.ResponseCallback.
The debugger usually does a good job of indicating where the exception came from. In the debugger, you can examine the exception details and look at the nessted InnerExceptions to find the root cause.
That said, if the exception is thrown from within the app.ApiAsync call, then the catch handler that you already have would catch any exceptions. By the looks of things in the SDK (I've only looked briefly), there are certain circumstances where exceptions are caught and forwarded to the callback in the Error property, which you are also checking.
By looking at the SDK code, it would seem that the exception being thrown is actually the FacebookOAuthException; is that the case? If that is the case, then it looks like this exception is never provided to the callback, but always thrown.
If you can give more details about exactly what the exception type is and where it's thrown/caught, I might be able to give a more useful answer.
Trying to catch the exception in App.UnhandledException does not work as it is on a different thread. But you can play with the 'error reason' property from authResult before doing the query and so you will avoid to have the exception thrown.
private void FacebookLoginBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
FacebookAuthenticationResult authResult;
if (FacebookAuthenticationResult.TryParse(e.Uri, out authResult))
{
if (authResult.ErrorReason == "user_denied")
{
// do something.
}
else
{
fbApp.Session = authResult.ToSession();
loginSucceeded();
}
}
I had the following:
$feedUrl = 'http://www.something.org/?feed=rss2';
$feed = Zend_Feed_Reader::import($feedUrl);
$lastNews = array();
//etc...
return $lastNews;
The problem was that, if the feed didn't exist for somereason, Zend will throw an exception and all my website will stay useless with that exception message.
I end up doing like this:
try {
$feedUrl = 'http://www.something.org/?feed=rss2';
$feed = Zend_Feed_Reader::import($feedUrl);
$lastNews = array();
//etc...
return $lastNews;
}
catch (Exception $e)
{
return false;
}
It works, but I just made up this. Not sure if it's ok. Any suggestions?
Regards,
MEM
That essentially the way you handle an Exception. Im not sure if i would return false, my preference would probably be for an empty array so that i dont have to have an if statement encapsulating loops that use the return value... but thats entirely up to you.
The only other exception would be to use a more specific exception like
try {
}
catch(Zend_Feed_Reader_Exception)
{
}
This way if a different error occurs you can handle it in a different fashion. Im not sure that exception actually exists but there is probably one or more exceptions unique tot he Zend_Feed component. Take a look at the code or docs to figure out which one(s) you want to catch and handle.
I'm currently building an iPhone app based on Gsoap toolkit to connect to a webservice. Everything works fine except when I try to connect to my service after disconnecting and reconnecting 3g on the device, I get :
SOAP 1.1 fault: SOAP-ENV:Client [no subcode]
"Connection refused"
Detail: connect failed in tcp_connect()
Working through the debugger shows that the error comes from connect() method of socket.h.
I don't really understand, when I launch another app like safari, the device is connected to the Internet. And after loading a web page, my app's connection works fine.
Here is the code I'm using :
//GSoap initialization
struct soap soap;
soap_init(&soap);
soap.connect_timeout = 0;
soap.send_timeout = 0;
soap.recv_timeout = 0;
// objects request & response
// struct types can be foundin soapStub.h
struct _ns1__GetAuthentification requete;
struct _ns1__GetAuthentificationResponse reponse;
// init request
requete.ConnectPass = (char *) [connectPass UTF8String];
requete.Login = (char *) [login UTF8String];
requete.Password = (char *) [password UTF8String];
requete.soap = &soap;
// request callback. returns SOAP_OK if something has been returned
if(soap_call___ns1__GetAuthentification(&soap,NULL,NULL, &requete,&reponse) == SOAP_OK){
//then we build the result
NSLog(#"Yay!");
soap_end(&soap); // remove deserialized data and clean up
soap_done(&soap); // detach the gSOAP environment
return authResult;
}
else {
//NSLog(#"Soap Error : GetAuthentification");
// We try to see if there's any problem. #catch statements are here just to keep note of the concerned
// exceptions for each request. No utility for the code.
#try {
[self processFault:&soap];
}
#catch (MMWrongId * e) {
#throw e;
}
#catch (MMConnectionFailed * e) {
#throw e;
}
#catch (MMGetAuthentificationFault * e) {
#throw e;
}
return nil;
}
Am I missing any particular flag/option?
For those who encounter the same issue, I got a solution. Michael Lasmanis has been a huge help for this one. Here is his answer :
this is one of the reasons i no longer recommend gsoap for iphone new iphone developers. gsoap uses the lower bsd sockets and bypasses the higher level iphone apis. it is the higher level api that manage the state of the internet connectivity which is why if you start safari first, then everything works. the easiest workaround is to use nsurlconnection to open a http connect to a well know site before calling gsoap.
I'm trying to use NSAssert throughout my iPhone app so that if an unexpected condition occurs, the application fails-fast and crashes with a meaningful message in the crash log.
This works fine if the failing NSAssert is on the main thread, as it raises NSInternalInconsistencyException by default which is uncaught and stops execution. But I'm also doing processing in background threads, in which case the NSAssert just aborts the thread, but the programming keeps running.
My current solution is to catch and rethrow the exception in the main thread (in this case, NSOperation's main method):
- (void)main {
#try {
int x = 14;
...
NSAssert1(x > 20, #"x should be greater than 20, was %d", x);
...
}
#catch (NSException *e) {
[e performSelectorOnMainThread:#selector(raise) withObject:nil waitUntilDone:YES];
}
}
Is there a better way? Perhaps using a custom NSAssertionHandler?
I know I could just use C's assert with a static annotation:
assert(x > 20 && "x should be greater than 20");
But this doesn't allow me to show what the actual failing value of x is.
You can replace the NSAssert with a test code followed by an exception raise. This way, if the assertion failed, an exception will be thrown, catched by the #catch block and re-raised on the main thread.
Note: You can even define a C macro, in order to provide a compact form.