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.
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
Here's the code:
ALint cProcessedBuffers = 0;
ALenum alError = AL_NO_ERROR;
alGetSourcei(m_OpenALSourceId, AL_BUFFERS_PROCESSED, &cProcessedBuffers);
if((alError = alGetError()) != AL_NO_ERROR)
{
throw "AudioClip::ProcessPlayedBuffers - error returned from alGetSroucei()";
}
alError = AL_NO_ERROR;
if (cProcessedBuffers > 0)
{
alSourceUnqueueBuffers(m_OpenALSourceId, cProcessedBuffers, arrBuffers);
if((alError = alGetError()) != AL_NO_ERROR)
{
throw "AudioClip::ProcessPlayedBuffers - error returned from alSourceUnqueueBuffers()";
}
}
The call to alGetSourcei returns with cProcessedBuffers > 0, but the following call to alSourceUnqueueBuffers fails with an INVALID_OPERATION. This in an erratic error that does not always occur. The program containing this sample code is a single-threaded app running in a tight loop (typically would be sync'ed with a display loop, but in this case I'm not using a timed callback of any sort).
Try alSourceStop(m_OpenALSourceId) first.
Then alUnqueueBuffers(), and after that, Restart playing by alSourcePlay(m_OpenALSourceId).
I solved the same problem by this way. But I don't know why have to do so in
Mentioned in this SO thread,
If you have AL_LOOPING enabled on a streaming source the unqueue operation will fail.
The looping flag has some sort of lock on the buffers when enabled. The answer by #MyMiracle hints at this as well, stopping the sound releases that hold, but it's not necessary..
AL_LOOPING is not meant to be set on a streaming source, as you manage the source data in the queue. Keep queuing, it will keep playing. Queue from the beginning of the data, it will loop.
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.
I have two methods that I need to run, lets call them metA and metB.
When I start coding this app, I called both methods without using threads, but the app started freezing, so I decided to go with threads.
metA and metB are called by touch events, so they can occur any time in any order. They don't depend on each other.
My problem is the time it takes to either threads start running. There's a lag between the time the thread is created with
[NSThread detachNewThreadSelector:#selector(.... bla bla
and the time the thread starts running.
I suppose this time is related to the amount of time required by iOS to create the thread itself. How can I speed this? If I pre create both threads, how do I make them just do their stuff when needed and never terminate? I mean, a kind of sleeping thread that is always alive and works when asked and sleeps after that?
thanks.
If you want to avoid the expensive startup time of creating new threads, create both threads at startup as you suggested. To have them only run when needed, you can have them wait on a condition variable. Since you're using the NSThread class for threading, I'd recommend using the NSCondition class for condition variables (an alternative would be to use the POSIX threading (pthread) condition variables, pthread_cond_t).
One thing you'll have to be careful of is if you get another touch event while the thread is still running. In that case, I'd recommend using a queue to keep track of work items, and then the touch event handler can just add the work item to the queue, and the worker thread can process them as long as the queue is not empty.
Here's one way to do this:
typedef struct WorkItem
{
// information about the work item
...
struct WorkItem *next; // linked list of work items
} WorkItem;
WorkItem *workQueue = NULL; // head of linked list of work items
WorkItem *workQueueTail = NULL; // tail of linked list of work items
NSCondition *workCondition = NULL; // condition variable for the queue
...
-(id) init
{
if((self = [super init]))
{
// Make sure this gets initialized before the worker thread starts
// running
workCondition = [[NSCondition alloc] init];
// Start the worker thread
[NSThread detachNewThreadSelector:#selector(threadProc:)
toTarget:self withObject:nil];
}
return self;
}
// Suppose this function gets called whenever we receive an appropriate touch
// event
-(void) onTouch
{
// Construct a new work item. Note that this must be allocated on the
// heap (*not* the stack) so that it doesn't get destroyed before the
// worker thread has a chance to work on it.
WorkItem *workItem = (WorkItem *)malloc(sizeof(WorkItem));
// fill out the relevant info about the work that needs to get done here
...
workItem->next = NULL;
// Lock the mutex & add the work item to the tail of the queue (we
// maintain that the following invariant is always true:
// (workQueueTail == NULL || workQueueTail->next == NULL)
[workCondition lock];
if(workQueueTail != NULL)
workQueueTail->next = workItem;
else
workQueue = workItem;
workQueueTail = workItem;
[workCondition unlock];
// Finally, signal the condition variable to wake up the worker thread
[workCondition signal];
}
-(void) threadProc:(id)arg
{
// Loop & wait for work to arrive. Note that the condition variable must
// be locked before it can be waited on. You may also want to add
// another variable that gets checked every iteration so this thread can
// exit gracefully if need be.
while(1)
{
[workCondition lock];
while(workQueue == NULL)
{
[workCondition wait];
// The work queue should have something in it, but there are rare
// edge cases that can cause spurious signals. So double-check
// that it's not empty.
}
// Dequeue the work item & unlock the mutex so we don't block the
// main thread more than we have to
WorkItem *workItem = workQueue;
workQueue = workQueue->next;
if(workQueue == NULL)
workQueueTail = NULL;
[workCondition unlock];
// Process the work item here
...
free(workItem); // don't leak memory
}
}
If you can target iOS4 and higher, consider using blocks with Grand Central Dispatch asynch queue, which operates on background threads which the queue manages... or for backwards compatibility, as mentioned use NSOperations inside an NSOperation queue to have bits of work performed for you in the background. You can specify exactly how many background threads you want to support with an NSOperationQueue if both operations have to run at the same time.
EDIT: Issue has been solved(partially):It is a simulator bug. I've compiled and tested this on two devices with iOS 3.1.3 and 4.0. The exception was handled correctly. Be careful, the simulator is your enemy!
this is driving me crazy. I don't know how to enable exception handling in my project. Look at the code and debugger output below.
My Goal is to catch the exception, not correcting the code so the exception is handled and the app doesn't crash.
I'm using XCode 3.2.3, iPhone SDK 4 final. I have just created a simple view based iPhone App to test this.
I have looked in my project settings and yes the switch "Enable Objective-C Exceptions" is checked. I am using GCC 4.2.
When I look at the build process in detail the compiler flag -fno-objc-exceptions is not within the list of arguments!
What am I missing here?
Thanks in advance
Nick
NSArray * foo = [[NSArray alloc] init];
#try {
NSLog(#"trying...");
[foo objectForKey:#"yeah"];
}
#catch (NSException * e) {
NSLog(#"catching %# reason %#", [e name], [e reason]);
}
#finally {
NSLog(#"finally");
}
leads to
trying...
-[__NSArrayI objectForKey:]: unrecognized selector sent to instance 0x5d5f780
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI objectForKey:]: unrecognized selector sent to instance 0x5d5f780'
*** Call stack at first throw:
(
0 CoreFoundation 0x02393919 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x024e15de objc_exception_throw + 47
2 CoreFoundation 0x0239542b -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
3 CoreFoundation 0x02305116 ___forwarding___ + 966
4 CoreFoundation 0x02304cd2 _CF_forwarding_prep_0 + 50
...
)
terminate called after throwing an instance of 'NSException'
Whether the catch nor the finally block is ever reached.
Quote from How do I catch global exceptions? :
"objc_exception_throw is not an exception. It is the function that throws Objective-C exceptions. Similarly, EXC_ARITHMETIC is not an Objective-C exception; it is a Mach (kernel) exception, meaning that your app tried to do something completely invalid. – Peter Hosey May 14 at 9:14"
That thread does have a link to a solution for your problem though, it appears. The link goes to http://www.restoroot.com/Blog/2008/10/20/crash-reporter-for-iphone-applications-part-2/ which looks a little risky, but if it works, it might be worth it for you.
There are bug reports related to this, e.g.: http://www.openradar.me/8081169 (posted earlier this month)
(Updated to summarize information from comments below.)
If I understand your problem right.
Your Try/ catch block is working correctly.
It is trying to run your code, and catches an error.
You need to decide what to do when it catches an error and code for it within the block.
I normally do that in the CATCH part. As the finally bit will execute regardless of an exception or not being thrown.
Your example code is catching the NSException exception but not the one being thrown, NSInvalidArgumentException. You might have better luck if you look for that specific exception.
NSArray * foo = [[NSArray alloc] init];
#try {
NSLog(#"trying...");
[foo objectForKey:#"yeah"];
}
#catch (NSInvalidArgumentException *e) {
NSLog(#"Invalid argument called.");
}
#catch (NSException * e) {
NSLog(#"catching %# reason %#", [e name], [e reason]);
}
#finally {
NSLog(#"finally");
}
I don't have any way of testing it myself right now, though.
See http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocExceptionHandling.html for more information.