I use HessianKit to communicate with server. In the situation of network or server down Hessian will throw exception, so I put every Hessian call in a #try ... #catch block. Everything worked fine until I upgraded Xcode from 3.2.2 to 3.2.3. I wrote the some testing code and found under Xcode 3.2.3, catch exception would be failed if the exception was thrown from a proxy object.
MyProxy.h:
#interface MyProxy : NSProxy {
}
#end
MyProxy.m:
#implementation MyProxy
- (id)init {
return self;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
NSLog(#"Call method %#", NSStringFromSelector([invocation selector]));
[NSException raise:#"MyException" format:#"this is an exception"];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
.....
}
#end
Code using MyProxy:
#try {
MyProxy *p = [[MyProxy alloc] init];
[p doSomething];
[p release];
}
#catch (NSException * e) {
NSLog(#"%#", e);
}
When these code build under xcode 3.2.2, the exception can be catched correctly. But under xcode 3.2.3, the program terminated after output following on the console:
2010-09-08 21:09:29.877 BriefCase[34651:40b] Call method doSomgthing
2010-09-08 21:09:29.879 BriefCase[34651:40b] *** Terminating app due to uncaught exception 'MyException', reason: 'this is an exception'
2010-09-08 21:09:29.880 BriefCase[34651:40b] Stack: (
45955152,
47113004,
45692683,
45692522,
151932,
45426420,
45423090,
9352,
4417860,
4421967,
4447550,
4429047,
4461016,
53399932,
45234332,
45230248,
4420129,
4453234,
8812,
8666
)
terminate called after throwing an instance of 'NSException'
Program received signal: “SIGABRT”.
What can I do?
I filed a bug with Apple, and the reply is:
It has been determined that this is a known issue, which is currently being investigated by engineering. This issue has been filed in our bug database under the original Bug ID# 7995323.
Maybe your project/target/executable settings have been messed up?
Is the "Enable Objective-C Exceptions" box ticked for your configuration/target/etc?
If it is, maybe you should file a bug with Apple here.
Related
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.
I modified a function in objective C to throw errors. I then want to catch these errors in Swift. I implemented the Bridging function, and it looks that everything is in order. I am getting the error:
Consecutive statements on a line must be separated by ';'
just right after the command:
try rfduino.send(data)
If I don't use try, it starts asking me for a second parameter. The function worked before I implemented this error handling. I am using XCode 6.4.
I also read in http://blog.benjamin-encz.de/swift-error-handling-and-objective-c-interop-in-depth/
that XCode only translate the function to a Swift-like error throwing function when the function returns a BOOL or an ObjectiveC type. I also tried that.
What can it be?
My .h file looks like:
- (BOOL)send:(NSData *)data
error:(NSError**) errorPtr;
in .m file:
- (BOOL)send:(NSData *)data
error:(NSError**)errorPtr
{
if (! loadedService) {
if (errorPtr) {
*errorPtr = [NSError errorWithDomain:NSCocoaErrorDomain
code:NSFileNoSuchFileError
userInfo:#{NSLocalizedDescriptionKey: NSLocalizedString(#"No Device Connected.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(#"The connection is not loaded.", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(#"Try disconnecting and connecting again", nil)
}];
} else {
#throw [NSException exceptionWithName:#"sendDataNoDevice" reason:#"please wait for ready callback" userInfo:nil];
}
return(NO);
}
if ([data length] > max_data) {
if (errorPtr) {
*errorPtr = [NSError errorWithDomain:NSCocoaErrorDomain
code:NSFileNoSuchFileError
userInfo:#{NSLocalizedDescriptionKey: NSLocalizedString(#"No Device Connected.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(#"The connection is not loaded.", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(#"Try disconnecting and connecting again", nil)
}];
} else {
#throw [NSException exceptionWithName:#"sendDataTooLarge" reason:#"max data size exceeded" userInfo:nil];
}
return(NO);
}
[peripheral writeValue:data forCharacteristic:send_characteristic type:CBCharacteristicWriteWithoutResponse];
return(YES);
}
thanks!
You have to update Xcode.
Consecutive statements... usually appears when Xcode doesn't understand the syntax: indeed do try catch is only available in Xcode 7+ with Swift 2.
I copied some good pdf viewing code from Julius Oklamcak. https://github.com/vfr/Viewer
It works great in his app when I do not modify it, but it gets messed up when I put it in my own app.
I removed the print, bookmark, and tile view buttons fyi.
The DONE button is not working.
Here is the error I am receiving:
2012-12-18 10:01:45.857 TeacherTableView4[1147:907] *** Assertion failure in -[ReaderViewController tappedInToolbar:doneButton:], (...my path to)/TeacherTableView4/ReaderViewController.m:844
2012-12-18 10:01:45.859 TeacherTableView4[1147:907] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Delegate must respond to -dismissReaderViewController:'
*** First throw call stack:
(0x371b52a3 0x3bd9497f 0x371b515d 0x3a2682af 0x40967 0x3c9f1 0x3676f0a5 0x3676f057 0x3676f035 0x3676e8eb 0x3676ede1 0x366975f1 0x36684801 0x3668411b 0x34a4f5a3 0x34a4f1d3 0x3718a173 0x3718a117 0x37188f99 0x370fbebd 0x370fbd49 0x34a4e2eb 0x366d82f9 0x414bd 0x36557b20)
libc++abi.dylib: terminate called throwing an exception
Here is the code:
- (void)tappedInToolbar:(ReaderMainToolbar *)toolbar doneButton:(UIButton *)button
{
#ifdef DEBUGX
NSLog(#"%s", __FUNCTION__);
#endif
#if (READER_STANDALONE == FALSE) // Option
[document saveReaderDocument]; // Save any ReaderDocument object changes
[[ReaderThumbQueue sharedInstance] cancelOperationsWithGUID:document.guid];
[[ReaderThumbCache sharedInstance] removeAllObjects]; // Empty the thumb cache
//COMMENTED OUT BY ME
//if (printInteraction != nil) [printInteraction dismissAnimated:NO]; // Dismiss
if ([delegate respondsToSelector:#selector(dismissReaderViewController:)] == YES)
{
[delegate dismissReaderViewController:self]; // Dismiss the ReaderViewController
}
else // We have a "Delegate must respond to -dismissReaderViewController: error"
{
NSAssert(NO, #"Delegate must respond to -dismissReaderViewController:");
}
#endif // end of READER_STANDALONE Option
}
I would really appreciate some help with this guys. Thanks!
I ran into the same problem using this PDF viewing code. I simply added this method
- (void)dismissReaderViewController:(ReaderViewController *) viewController
{
[self dismissModalViewControllerAnimated:YES];
}
to the .m file where I'm calling the - (IBAction)didClickOpen from.
This is mainly due to the fact that in ReaderViewController.m the delegate expects to see dismissReaderViewController declared in your implementation file. Otherwise, it will throw that error you're getting.
Note: Use [self dismissViewControllerAnimated:YES completion:nil]; for iOS6 or later since dismissModalViewControllerAnimated:YES was deprecated :-)
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.
I'm getting this stack trace in the output window of XCode 4:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Page bottom]: unrecognized selector sent to instance 0xfbdb1f0'
However, the calling code has a try catch
#try {
[self restoreStateWithControlSurfaces:result];
}
#catch (NSException *exception) {
NSLog(#"Failed at restoreStateWithControlSurfaces %#", exception);
retVal = NO;
}
It might have something to do with NSHangOnOtherExceptionMask but I'm not sure how this fits together. How can I get my catch block to work? This is in the simulator for iPad 4.2.
A bug has been reported that prevents NSInvalidArgumentException from being caught. This bug appears to affect the simulator only.