Swift Catch Runtime Exceptions - objective-c

I'm running fuzz testing on an application, and so am looking specifically for runtime errors that aren't handled. The application is written in both ObjC and Swift, but the unit tests are written in Swift.
I understand the basis of swift isn't to catch arbitrary runtime exceptions, but this is purely for unit tests. How do I catch runtime these exceptions (i.e. index out of bounds etc.)

To catch Obj-C exceptions in Swift, I am using a simple Obj-C class:
#import "ObjC2Swift.h"
#implementation ObjC
+ (id)catchException:(id(^)())tryBlock error:(__autoreleasing NSError **)error {
#try {
id result = tryBlock();
return result;
}
#catch (NSException *exception) {
if (error) {
*error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:exception.userInfo];
}
return nil;
}
}
#end
In Swift called as
let result = try? ObjC.catchException { ... dangerous code here ... }
You might need a different variant for blocks that don't return anything.
Not to be abused. Obj-C exception are evil and I am using this only because I need a library that uses them.

Related

XPC and Exception handling

I have an LaunchAgent using HockeyApp for crash reporting. Now I noticed that uncaught exception where not reported by HockeyApp, like they would have been in a normal macOS app.
For example:
- (void)upperCaseString:(NSString *)aString withReply:(void (^)(NSString *))reply {
NSArray *array = [NSArray array];
reply([array objectAtIndex:23]);
}
Never reaches NSUncaughtExceptionHandler, but the console logs:
<NSXPCConnection: 0x7fe97dc0f110> connection from pid 44573: Warning: Exception caught during invocation of received message, dropping incoming message and invalidating the connection.
The question is how to get unhandled exception reported with HockeyApp.
Problem:
XPC seems to have its own #try #catch block, which catches unhandled exceptions inside a method, logs the exception and then the calls -[NSXPCConnection interruptionHandler].
This issue is reported to Apple under rdar://48543049.
NOTE: These are not copy & past solutions, carefully evaluate your crash reporting framework. I link to implementation details of PLCrashReporter.
Solution A:
#try #catch block:
- (void)upperCaseString:(NSString *)aString withReply:(void (^)(NSString *))reply {
#try {
NSArray *array = [NSArray array];
reply([array objectAtIndex:23]);
} #catch (NSException *exception) {
NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler();
if (handler) {
handler(exception);
}
}
}
Discussion
HockeyApp uses PLCrashReporter for crash reporting. PLCrashReporter registers an NSUncaughtExceptionHandler (code). So the above code will forward the exception to the PLCrashReporter exception handler and terminates (code) the XPC.
Mattie suggest to #throw the exception again, to trigger the internal XPC #catch block and possible internal clean-up and logging. This is something to consider. Especially if you have a custom interruptionHandler on NSXPCConnection in the LaunchAgent/Server side of the connection!
For now I side with not throwing it again, because my XPC is complete stateless and should be fine just crashing.
The downside to Solution A is that every method exposed via XPC requires this #try #catch block.
Solution B:
Use a NSProxy that catches all unhandled exceptions as NSXPCConnection exportObject:
#interface LOUncaughtExceptionHandlerProxy : NSProxy {
NSObject *_object;
}
#end
#implementation LOUncaughtExceptionHandlerProxy
- (instancetype)initWithObject:(NSObject *)object
{
NSParameterAssert(object);
_object = object;
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
return [_object methodSignatureForSelector:selector];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
#try {
[invocation invokeWithTarget:_object];
} #catch (NSException *exception) {
NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler();
if (handler) {
handler(exception);
}
}
}
#end
Setup within the NSXPCListener listener:
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
XPC *exportedObject = [XPC new];
LOUncaughtExceptionHandlerProxy *proxy = [[LOUncaughtExceptionHandlerProxy alloc] initWithObject:exportedObject];
newConnection.exportedObject = proxy;
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:#protocol(XPCProtocol)];
[newConnection resume];
return YES;
}
All details of Solution A apply to Solution B.
Solution Z:
On macOS is possible to use the ExceptionHandling.framework, the problems with it are very well outlined in BITCrashExceptionApplication.h.
Discussion
It is never a good sign when a framework is not ported to iOS. Also note Matties comment:
I've had interactions with Apple that directly indicate that ExceptionHandling.framework is no longer supported. And, in my experience while working on Crashlytics, it had some fundamental interoperability issues beyond what is indicated in that quoted header.

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.

Objective-C Framework Error Handling

I'm creating a framework for use by a Cocoa Application on 10.6 and later.
The purpose of the framework is to parse a text file.
Obviously, there are errors that could occur, such as file not found, permissions issues, etc.
What is the right way to handle errors within the framework and notify the host application?
My thoughts were:
Do nothing and let the host application catch any exceptions.
Have the host application register its first responder with the framework, catch any exceptions, convert them into NSError and pass them to the host app's responder chain.
Do either of those options make sense? Are there other options? What's the right way to handle this?
I have read the error and exception handling guides, but they don't cover this situation and only describe error handling within the application itself.
I would say the correct way is to use NSError directly yourself in all methods that can error. I have done this recently with a utility class I created, and it works very well. You then allow the application to decide what do to with the error (crash, log, something else) and the framework doesn't need to worry.
Here are the private class methods I used to create the error objects, allowing for underlying POSIX errors (errno etc.):
#pragma mark - Private Methods
- (NSError *)error:(NSString *)localizedDescription
code:(EZipFileError)code
underlyingError:(NSError *)underlyingError
{
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
[errorDetail setValue:localizedDescription forKey:NSLocalizedDescriptionKey];
if (underlyingError != nil)
{
[errorDetail setValue:underlyingError forKey:NSUnderlyingErrorKey];
}
return [NSError errorWithDomain:#"MyErrorDomain"
code:(NSInteger)code
userInfo:errorDetail];
}
- (NSError *)error:(NSString *)localizedDescription
code:(EZipFileError)code
{
return [self error:localizedDescription
code:code
underlyingError:nil];
}
- (NSError *)error:(NSString *)localizedDescription
code:(EZipFileError)code
posixError:(int)posixError
{
NSMutableDictionary *underlyingErrorDetail = [NSMutableDictionary dictionary];
[underlyingErrorDetail setValue:[NSString stringWithUTF8String:strerror(posixError)]
forKey:NSLocalizedDescriptionKey];
NSError *underlyingError = [NSError errorWithDomain:NSPOSIXErrorDomain
code:posixError
userInfo:underlyingErrorDetail];
return [self error:localizedDescription
code:code
underlyingError:underlyingError];
}
Which is used as follows:
if (![self isOpen])
{
if (error != NULL)
{
*error = [self error:#"File is not open"
code:ErrorNotOpen];
}
return nil;
}
Here's an example that uses the underlying POSIX error version of the method:
filefp = fopen([filename UTF8String], "rb");
if (filefp == NULL)
{
if (error != NULL)
{
*error = [self error:#"Failed to open file"
code:ErrorOpenFileFailed
posixError:errno];
}
return NO;
}
Exceptions should be used only for terminal errors in Objective-C. More specifically, Cocoa and Cocoa Touch don't guarantee that exceptions thrown across their boundaries will come out the other side, so you shouldn't use exceptions for error handling.
The right way to report errors to the caller is via an NSError object. You'll notice that many Cocoa and Cocoa Touch methods include a NSError* parameter for exactly that purpose.

throwing an exception in objective-c/cocoa

What's the best way to throw an exception in objective-c/cocoa?
I use [NSException raise:format:] as follows:
[NSException raise:#"Invalid foo value" format:#"foo of %d is invalid", foo];
A word of caution here. In Objective-C, unlike many similar languages, you generally should try to avoid using exceptions for common error situations that may occur in normal operation.
Apple's documentation for Obj-C 2.0 states the following: "Important: Exceptions are resource-intensive in Objective-C. You should not use exceptions for general flow-control, or simply to signify errors (such as a file not being accessible)"
Apple's conceptual Exception handling documentation explains the same, but with more words: "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."
The reasons for this is partly to adhere to programming idioms in Objective-C (using return values in simple cases and by-reference parameters (often the NSError class) in more complex cases), partly that throwing and catching exceptions is much more expensive and finally (and perpaps most importantly) that Objective-C exceptions are a thin wrapper around C's setjmp() and longjmp() functions, essentially messing up your careful memory handling, see this explanation.
#throw([NSException exceptionWith…])
Xcode recognizes #throw statements as function exit points, like return statements. Using the #throw syntax avoids erroneous "Control may reach end of non-void function" warnings that you may get from [NSException raise:…].
Also, #throw can be used to throw objects that are not of class NSException.
Regarding [NSException raise:format:]. For those coming from a Java background, you will recall that Java distinguishes between Exception and RuntimeException. Exception is a checked exception, and RuntimeException is unchecked. In particular, Java suggests using checked exceptions for "normal error conditions" and unchecked exceptions for "runtime errors caused by a programmer error." It seems that Objective-C exceptions should be used in the same places you would use an unchecked exception, and error code return values or NSError values are preferred in places where you would use a checked exception.
I think to be consistant it's nicer to use #throw with your own class that extends NSException. Then you use the same notations for try catch finally:
#try {
.....
}
#catch{
...
}
#finally{
...
}
Apple explains here how to throw and handle exceptions:
Catching Exceptions
Throwing Exceptions
Since ObjC 2.0, Objective-C exceptions are no longer a wrapper for C's setjmp() longjmp(), and are compatible with C++ exception, the #try is "free of charge", but throwing and catching exceptions is way more expensive.
Anyway, assertions (using NSAssert and NSCAssert macro family) throw NSException, and that sane to use them as Ries states.
Use NSError to communicate failures rather than exceptions.
Quick points about NSError:
NSError allows for C style error codes (integers) to clearly identify the root cause and hopefully allow the error handler to overcome the error. You can wrap error codes from C libraries like SQLite in NSError instances very easily.
NSError also has the benefit of being an object and offers a way to describe the error in more detail with its userInfo dictionary member.
But best of all, NSError CANNOT be thrown so it encourages a more proactive approach to error handling, in contrast to other languages which simply throw the hot potato further and further up the call stack at which point it can only be reported to the user and not handled in any meaningful way (not if you believe in following OOP's biggest tenet of information hiding that is).
Reference Link: Reference
This is how I learned it from "The Big Nerd Ranch Guide (4th edition)":
#throw [NSException exceptionWithName:#"Something is not right exception"
reason:#"Can't perform this operation because of this or that"
userInfo:nil];
You can use two methods for raising exception in the try catch block
#throw[NSException exceptionWithName];
or the second method
NSException e;
[e raise];
I believe you should never use Exceptions to control normal program flow. But exceptions should be thrown whenever some value doesn't match a desired value.
For example if some function accepts a value, and that value is never allowed to be nil, then it's fine to trow an exception rather then trying to do something 'smart'...
Ries
You should only throw exceptions if you find yourself in a situation that indicates a programming error, and want to stop the application from running. Therefore, the best way to throw exceptions is using the NSAssert and NSParameterAssert macros, and making sure that NS_BLOCK_ASSERTIONS is not defined.
Sample code for case: #throw([NSException exceptionWithName:...
- (void)parseError:(NSError *)error
completionBlock:(void (^)(NSString *error))completionBlock {
NSString *resultString = [NSString new];
#try {
NSData *errorData = [NSData dataWithData:error.userInfo[#"SomeKeyForData"]];
if(!errorData.bytes) {
#throw([NSException exceptionWithName:#"<Set Yours exc. name: > Test Exc" reason:#"<Describe reason: > Doesn't contain data" userInfo:nil]);
}
NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
options:NSJSONReadingAllowFragments
error:&error];
resultString = dictFromData[#"someKey"];
...
} #catch (NSException *exception) {
  NSLog( #"Caught Exception Name: %#", exception.name);
  NSLog( #"Caught Exception Reason: %#", exception.reason );
resultString = exception.reason;
} #finally {
completionBlock(resultString);
}
}
Using:
[self parseError:error completionBlock:^(NSString *error) {
NSLog(#"%#", error);
}];
Another more advanced use-case:
- (void)parseError:(NSError *)error completionBlock:(void (^)(NSString *error))completionBlock {
NSString *resultString = [NSString new];
NSException* customNilException = [NSException exceptionWithName:#"NilException"
reason:#"object is nil"
userInfo:nil];
NSException* customNotNumberException = [NSException exceptionWithName:#"NotNumberException"
reason:#"object is not a NSNumber"
userInfo:nil];
#try {
NSData *errorData = [NSData dataWithData:error.userInfo[#"SomeKeyForData"]];
if(!errorData.bytes) {
#throw([NSException exceptionWithName:#"<Set Yours exc. name: > Test Exc" reason:#"<Describe reason: > Doesn't contain data" userInfo:nil]);
}
NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
options:NSJSONReadingAllowFragments
error:&error];
NSArray * array = dictFromData[#"someArrayKey"];
for (NSInteger i=0; i < array.count; i++) {
id resultString = array[i];
if (![resultString isKindOfClass:NSNumber.class]) {
[customNotNumberException raise]; // <====== HERE is just the same as: #throw customNotNumberException;
break;
} else if (!resultString){
#throw customNilException; // <======
break;
}
}
} #catch (SomeCustomException * sce) {
// most specific type
// handle exception ce
//...
} #catch (CustomException * ce) {
// most specific type
// handle exception ce
//...
} #catch (NSException *exception) {
// less specific type
// do whatever recovery is necessary at his level
//...
// rethrow the exception so it's handled at a higher level
#throw (SomeCustomException * customException);
} #finally {
// perform tasks necessary whether exception occurred or not
}
}
There is no reason not to use exceptions normally in objective C even to signify business rule exceptions. Apple can say use NSError who cares. Obj C has been around a long time and at one time ALL C++ documentation said the same thing. The reason it doesnt matter how expensive throwing and catching an exception is, is the lifetime of an exception is exceedingly short and...its an EXCEPTION to the normal flow. I have never heard anyone say ever in my life, man that exception took a long time to be thrown and caught.
Also, there are people that think that objective C itself is too expensive and code in C or C++ instead. So saying always use NSError is ill-informed and paranoid.
But the question of this thread hasnt yet been answered whats the BEST way to throw an exception. The ways to return NSError are obvious.
So is it: [NSException raise:... #throw [[NSException alloc] initWithName....
or #throw [[MyCustomException... ?
I use the checked/unchecked rule here slightly differently than above.
The real difference between the (using the java metaphor here) checked/unchecked is important --> whether you can recover from the exception. And by recover I mean not just NOT crash.
So I use custom exception classes with #throw for recoverable exceptions, because
its likely I will have some app method looking for certain types of failures in multiple
#catch blocks. For example if my app is an ATM machine, I would have a #catch block for the
"WithdrawalRequestExceedsBalanceException".
I use NSException:raise for runtime exceptions since I have no way to recover from the exception,
except to catch it at a higher level and log it. And theres no point in creating a custom class for that.
Anyway thats what I do, but if there's a better, similarly expressive way I would like to know as well. In my own code, since I stopped coding C a hella long time ago I never return an NSError even if I am passed one by an API.