An iPad app that runs fine under IOS3 fails under IOS4.2 It has a class that runs an http session from an operation queue and the failure is linked to this activity. Here is the console output:
Program received signal: “EXC_BAD_ACCESS”.
[Switching to thread 11523]
Running NSZombies enabled didn't reveal anything so I have been putting NSLog statements in the code and found that the crash occurs when a local variable is changed. Here is the code section:
self.currentOperation = [[[DeduceAccessOperation alloc] init] autorelease];
[self.currentOperation addObserver:self forKeyPath:#"isFinished"
options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
context:NULL];
NSLog (#"Start observer added");
[operationQueue addOperation:self.currentOperation];
NSLog (#"Start operation added");
NSLog(#"State is %d", self.status);
self.status = IEnablerServiceUpdating;
NSLog (#"State updated");
And here is the console log output:
2010-12-08 21:26:44.548 UCiEnabler[5180:307] Start observer added
2010-12-08 21:26:44.550 UCiEnabler[5180:307] Start operation added
2010-12-08 21:26:44.552 UCiEnabler[5180:307] State is 1
Program received signal: “EXC_BAD_ACCESS”.
[Switching to thread 11523]
It is like status has become read-only (It's property is declared as atomic and readwrite).
The other relevant piece of information is that a sub-view has just been changed and it triggers the call on the above routine. It's code is:
//Start the update
UCiEnablerAppDelegate *controller = (UCiEnablerAppDelegate *)[[UIApplication sharedApplication] delegate];
[controller deduceIEnablerServiceAccess];
controller.serviceBusy = TRUE; //1.04
Has anyone seen anything like this?
Has anyone ideas where to look next?
Regards
Robin
Here's the stack trace:
#0 0x34a80464 in objc_msgSend
#1 0x3119543e in NSKVOPendingNotificationCreate
#2 0x3119535a in NSKeyValuePushPendingNotificationPerThread
#3 0x3117009a in NSKeyValueWillChange
#4 0x311682c6 in -[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:]
#5 0x311cc718 in _NSSetIntValueAndNotify
#6 0x000097ce in -[IEnablerService startDeducingAccessState] at IEnablerService.m:55
#7 0x00002bc0 in -[UCiEnablerAppDelegate deduceIEnablerServiceAccess] at UCiEnablerAppDelegate.m:100
#8 0x0000a33e in -[RootViewControlleriPad animationDidStop:finished:context:] at RootViewController-iPad.m:43
#9 0x341bb336 in -[UIViewAnimationState sendDelegateAnimationDidStop:finished:]
NSOperationQueue in iOS 4.2 now uses GrandCentralDispatch when starting NSOperations. There's a Technical Q&A here
Pretty much the queue now calls the NSOperation subclass's start method in a new thread regardless of the isConcurrent property. In my experience it means that if you are using NSURLConnection inside a start method your code will no longer run because the thread start is running on doesn't have an NSRunLoop.
Apple says that this change doesn't break compatibility because you were supposed to spawn a new thread in the start method anyway.
For backwards compatibility you should change your subclass from using start to using run.
Related
I have the following situation where I create a GCD dispatch queue and in it I schedule an NSStream to the current NSRunLoop, as is required in its specification for it to emit delegate events, and then I make the run loop for that thread run using [[NSRunLoop currentRunLoop run].
This generates three possible scenarios:
Create a serial queue in which an initial write message is sent through the stream and other write messages are only sent when there's a delegate callback from the NSStream object, as attempting to write new messages without respecting this pattern (this would be desirable) will fail as the queue is locked by the run loop running.
Create a concurrent queue in which messages can be written to the stream freely, as blocks sent to the queue will be executed concurrently with the block that's running the run loop. However, while it is desirable to make writing messages and the run loop running concurrent, it certainly is not desirable to have to blocks in the queue running concurrently attempting to write at the same time to the stream.
Create two queues -- one responsible for keeping the run loop alive and receive read-from-stream callbacks and another one for sending asynchronous write messages to the stream. This would seem ideal, however it seems that the NSStream documentation specifically states that one should not attempt to read/write to a stream outside the thread it is scheduled in.
Given these scenarios none of which are ideal, how to solve these problems?
Late to the party, but instead of using runloops you can set the desired dispatch queue for your streams directly using
void CFReadStreamSetDispatchQueue(CFReadStreamRef stream, dispatch_queue_t q);
void CFWriteStreamSetDispatchQueue(CFWriteStreamRef stream, dispatch_queue_t q);
Where CFReadStreamRef can take a bridged NSInputStream and CFWriteStreamRef a bridged NSOutputStream. This way you don't have to schedule or unschedule runloops at all and your streams will run in the background.
Snippet from this Apple sample code:
CFReadStreamSetDispatchQueue((__bridge CFReadStreamRef) self.inputStream, self.queue);
CFWriteStreamSetDispatchQueue((__bridge CFWriteStreamRef) self.outputStream, self.queue);
In Swift, you can just directly call the functions:
CFReadStreamSetDispatchQueue(inputStream, streamQueue)
CFWriteStreamSetDispatchQueue(outputStream, streamQueue)
As you noted from the docs, when you have a run-loop-based API like NSStream, the general expectation is that all interaction with that object will occur on the thread that owns the run loop on which it's scheduled. I'm not sure there's really any benefit to mixing these two idioms (GCD and run loops) when it comes to working with NSStream.
Other than the main queue, GCD has no concept of thread-affinity, so unless the run loop you schedule the NSStream on happens to be the main thread run loop, there's no good way to use dispatch_async to schedule blocks for execution on that thread.
At the risk of stating the obvious, you should probably just use the standard methods for scheduling methods on other threads. -performSelector:onThread:withObject:waitUntilDone:modes: is the most obvious. If your confusion is that you want to work with blocks, it helps to know that heap-allocated blocks can be treated like Objective-C objects and implement the -invoke selector just like NSInvocations do. A trivial example relevant to your question might look like this:
#interface AppDelegate ()
{
NSThread* bgthread;
}
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Basic loop to get the background thread to run until you call -cancel on it
dispatch_block_t threadMain = [^{
NSThread* thread = [NSThread currentThread];
NSParameterAssert(![thread isMainThread]);
NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop];
NSPort* port = [NSPort port];
// If we dont register a mach port with the run loop, it will just exit immediately
[currentRunLoop addPort: port forMode: NSRunLoopCommonModes];
// Loop until the thread is cancelled.
while (!thread.cancelled)
{
[currentRunLoop runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]];
}
[currentRunLoop removePort: port forMode: NSRunLoopCommonModes];
[port invalidate];
port = nil;
} copy];
// Start the thread
bgthread = [[NSThread alloc] initWithTarget: threadMain selector: #selector(invoke) object: nil];
[bgthread start];
// Fetch the runloop, so you can schedule an NSStream on it...
__block NSRunLoop* runloopForStream = nil;
dispatch_block_t getrunloop = [^{
runloopForStream = [NSRunLoop currentRunLoop];
} copy];
// Dispatch synchronously, so that runloopForStream is populated before we continue...
[getrunloop performSelector: #selector(invoke) onThread: bgthread withObject: nil waitUntilDone: YES];
// Schedule your stream, etc.
NSOutputStream* mystream = ...; // Your code here...
[mystream scheduleInRunLoop: runloopForStream forMode: NSDefaultRunLoopMode];
// Then later, when you want to write some data...
NSData* dataToWrite = [NSMutableData dataWithLength: 100];
dispatch_block_t doWrite = [^{
[mystream write: dataToWrite.bytes maxLength: dataToWrite.length];
} copy];
// Dispatch asynchronously to thread
[doWrite performSelector: #selector(invoke) onThread: bgthread withObject: nil waitUntilDone: NO];
}
#end
Note that the -copy of the blocks is necessary to get them copied to the heap, otherwise they'll be deallocated when the declaring method goes out of scope.
I am having a gui/threading related problem in developing a cocoa user interface. The application is designed like this:
Main Thread (#1): parses arguments, loads plugins, etc.
Gui thread (#?): launches the gui, handles events, etc. Its the gui thread.
The Cocoa framework is non-thread safe, but enforces one rule, the GUI must run on the main thread. A assertion is used to check this. To try to go around this I implemented the run method myself (code below) following this - http://cocoawithlove.com/2009/01/demystifying-nsapplication-by.html - guide. But I am missing something. A window is opened, but stays blank (completely white). Although if I make the call in the main thread it works perfectly.
So basically I need to figure out what's missing.
- (void)run
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self finishLaunching];
shouldKeepRunning = YES;
do
{
[pool release];
pool = [[NSAutoreleasePool alloc] init];
NSEvent *event =
[self
nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantFuture]
inMode:NSDefaultRunLoopMode
dequeue:YES];
[self sendEvent:event];
[self updateWindows];
} while (shouldKeepRunning);
[pool release];
}
- (void)terminate:(id)sender
{
shouldKeepRunning = NO;
}
Don't. This approach will never work. Even if you fix your current problem (the window not drawing) you'll immediately run into another obscure, impossible-to-fix problem, and another, and another. Cocoa expects the GUI thread to be the main thread, end of story.
Do all in the background thread except updating the GUI. I see that you have only a line where you need to update the GUI. So do it the way you're doing it, except that you execute all GUI updates in the main thread:
dispatch_async(dispatch_get_main_queue(), ^
{
[self updateWindows];
});
Now I don't know what's updateWindows, I assumed that this wouldn't create a race condition.
Why not reverse the problem? Have the main thread spawn a thread (let's call this the app thread), then block before spawning the GUI. The app thread will parse arguments, load plugins, etc. After it's initialization is done, the app thread will signal the main thread to go ahead and launch the GUI.
I have an Cocoa Application (Mac OS X SDK 10.7) that is performing some processes via Grand Central Dispatch (GCD). These processes are manipulating some Core Data NSManagedObjects (non-document-based) in a manner that I believe is thread safe (creating a new managedObjectContext for use in this thread).
The problem I have is when the user tries to quit the application while the dispatch queue is still running.
The NSApplication delegate is being called before actually quitting.
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
I get an error "Could not merge changes." Which is somewhat expected since there are still operations being performed through the different managedObjectContext. I am then presented with the NSAlert from the template that is generated with a core data application.
In the Threading Programming Guide there is a section called "Be Aware of Thread Behaviors at Quit Time" which alludes to using replyToApplicationShouldTerminate: method. I'm having a little trouble implementing this.
What I would like is for my application to complete processing the queued items and then terminate without presenting an error message to the user. It would also be helpful to update the view or use a sheet to let the user know that the app is performing some action and will terminate when the action is complete.
Where and how would I implement this behavior?
Solution:
So I had a few different issues here.
I had blocks that were accessing core data in a dispatch_queue preventing my application from terminating gracefully.
When I tried to add a new item to the dispatch_queue a new instance of the dispatch_queue was started on a new thread.
What I did to solve this was use NSNotificationCenter in my AppDelegate (where (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender was being called. In the template code that Core Data generates add the following:
// Customize this code block to include application-specific recovery steps.
if (error) {
// Do something here to add queue item in AppController
[[NSNotificationCenter defaultCenter] postNotificationName:#"TerminateApplicationFromQueue" object:self];
return NSTerminateLater;
}
Then in AppController add an observer for the notification (I added this to awakeFromNib):
- (void)awakeFromNib {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(terminateApplicationFromQueue:) name:#"TerminateApplicationFromQueue" object:nil];
// Set initial state of struct that dispatch_queue checks to see if it should terminate the application.
appTerminating.isAppTerminating = NO;
appTerminating.isTerminatingNow = NO;
}
I have also created a struct that can be checked against to see if the user wants to terminate the application. (I set the initial state of the struct in awakeFromNib above). Place the struct after your #synthesize statements:
struct {
bool isAppTerminating;
bool isTerminatingNow;
} appTerminating;
Now for the long-running dispatch_queue that is preventing the app from gracefully terminating. When I initially create this dispatch_queue, a for loop is used to add the items that need updating. After this for loop is executed, I have tacked on another queue item that will check the struct to see if the app should terminate:
// Additional queue item block to check if app should terminate and then update struct to terminate if required.
dispatch_group_async(refreshGroup, trackingQueue, ^{
NSLog(#"check if app should terminate");
if (appTerminating.isAppTerminating) {
NSLog(#"app is terminating");
appTerminating.isTerminatingNow = YES;
}
});
dispatch_release(refreshGroup);
And the method to be called when the notification is received:
- (void)terminateApplicationFromQueue:(NSNotification *)notification {
// Struct to check against at end of dispatch_queue to see if it should shutdown.
if (!appTerminating.isAppTerminating) {
appTerminating.isAppTerminating = YES;
dispatch_queue_t terminateQueue = dispatch_queue_create("com.example.appname.terminate", DISPATCH_QUEUE_SERIAL); // or NULL
dispatch_group_t terminateGroup = dispatch_group_create();
dispatch_group_async(terminateGroup, terminateQueue, ^{
NSLog(#"termination queued until after operation is complete");
while (!appTerminating.isTerminatingNow) {
// add a little delay before checking termination status again
[NSThread sleepForTimeInterval:0.5];
}
NSLog(#"terminate now");
[NSApp replyToApplicationShouldTerminate:YES];
});
dispatch_release(terminateGroup);
}
}
I haven't dealt with this myself, but just from my reading of the docs, it looks like what you should do is:
Return NSTerminateLater from applicationShouldTerminate:. This lets the system know that your app isn't ready to terminate just yet, but will do so shortly.
Enqueue a "final" block on your dispatch queue. (You need to make sure that other blocks are not enqueued after this. This block will then be run after all the other work has been performed. Note the queue must be serial -- not one of the concurrent queues) for this to work correctly.) The "final" block should do [NSApp replyToApplicationShouldTerminate:YES];, which will complete the normal termination process.
There isn't any direct way to find out whether a GCD queue is still working. The only other thing that you can do (that I know of) to handle this is to put all of the blocks into a dispatch group, and then wait on the group in applicationShouldTerminate: (using dispatch_group_wait().
This question follows on from my other question on why my app isn't being brought down by exceptions.
The Problem
When an exception is thrown on the main thread via an Action, the app still doesn't crash.
As per Dave's answer to my original question, I've implemented the reportException category on NSApplication and set the uncaught exception handler.
Code
I've got the following in my app delegate, which I've hooked up to a button in my UI to test.
-(IBAction)crashOnMainThread:(id)sender {
[self performSelectorOnMainThread:#selector(crash) withObject:nil waitUntilDone:YES];
}
-(void)crash {
// To test out the exception handling
[NSException raise:NSInternalInconsistencyException format:#"This should crash the app."];
}
When I press the button, my app doesn't crash. When I look at the console log, I see this:
06/09/2010 14:12:25 EHTest1[26384] HIToolbox: ignoring exception 'This should crash the app.' that raised inside Carbon event dispatch
(
0 CoreFoundation 0x00007fff80ab4cc4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff819560f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff80ab4ae7 +[NSException raise:format:arguments:] + 103
3 CoreFoundation 0x00007fff80ab4a74 +[NSException raise:format:] + 148
4 EHTest1 0x00000001000010e3 -[EHTest1_AppDelegate crashLapsus] + 63
5 Foundation 0x00007fff88957c25 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 234
6 Foundation 0x00007fff8896ad48 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] + 143
7 EHTest1 0x0000000100001030 -[EHTest1_AppDelegate crashOnMainThread:] + 60
8 AppKit 0x00007fff85c7e152 -[NSApplication sendAction:to:from:] + 95
9 AppKit 0x00007fff85ca26be -[NSMenuItem _corePerformAction] + 365
** Snip **
It looks like Carbon is catching the exception, which is really annoying.
This suggests that for any action code, you need to immediately run it in a background thread so any exceptions are registered as uncaught. Huh? I've not seen any code that's structured like this.
What I've tried
Crashing the app with a delay so it's not connected to a UI element. It crashes fine.
I've tried installing a custom NSExceptionHandler using this in my app delegate:
-(BOOL)exceptionHandler:(NSExceptionHandler *)sender
shouldHandleException:(NSException *)exception
mask:(unsigned int)aMask {
abort();
return YES;
}
-(void)applicationWillFinishLaunching:(NSNotification *)aNotification {
NSExceptionHandler *handler = [NSExceptionHandler defaultExceptionHandler];
[handler setExceptionHandlingMask:NSLogAndHandleEveryExceptionMask];
[handler setDelegate:self];
}
the problem here is it crashes on every exception, whether it's caught or not.
If I try and check the mask and don't crash on a caught exception, I'm back to square 1 as it seems that HIToolbox catches the exception in exactly the same way as a try/catch block would.
Questions
How can I stop HIToolbox catching the exceptions so that my app uses the uncaught exception handler and crashes?
Is it OK to be running code that's in the same call stack as an action? Surely this is OK?
If it's not OK, what's the alternative?
This is driving me up the wall, so any help would be much appreciated.
I answered your last question on this subject, and ran into the same problem with Carbon's HIToolbox catching exceptions thrown by IBActions.
First, undo everything I mentioned in my previous answer. It doesn't work with IBActions for some reason. My hunch is that HIToolbox lives lower on the exception-handling-chain, and gets any IBAction/GUI exceptions before NSApplication has the opportunity to. Any custom exception-handling function you can register with NSSetUncaughtExceptionHandler() lives (I believe) at the top of the chain.
You're on the right track with NSExceptionHandling:
Add the ExceptionHandling.framework to your Xcode Project
#import "ExceptionHandlerDelegate.h" into your AppDelegate.m (or a custom Singleton exception class)
Inside AppDelegate.m:
// Very first message sent to class
+ (void)initialize
{
NSExceptionHandler *exceptionHandler = [NSExceptionHandler defaultExceptionHandler];
unsigned int handlingMask = NSLogAndHandleEveryExceptionMask;
[exceptionHandler setExceptionHandlingMask:handlingMask];
[exceptionHandler setDelegate:self];
// ...
}
#pragma mark -
#pragma mark NSExceptionHandler Delegate Methods
// Called 1st: Should NSExceptionHandler log the exception?
- (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldLogException:(NSException *)exception mask:(unsigned int)aMask
{
// ...log NSException if desired...
return NO; // Changes to YES if NSExceptionHandler should handle logging
}
// Called 2nd: Should NSExceptionHandler handle the exception?
- (BOOL)exceptionHandler:(NSExceptionHandler *)sender shouldHandleException:(NSException *)exception mask:(unsigned int)aMask
{
// ...crash your app here (e.g. [NSApp terminate:self]; )
// ...or handle the NSException and return YES/NO accordingly
return NO; // If app crashed, never gets returned - should crash before that
}
The NSLogAndHandleEveryExceptionMask flag tells NSExceptionHandler to capture every exception it can (for your app only, I believe), regardless of where on the exception chain it exists.
This means that #catch/#try/#finally blocks won't work, because the NSHandleOtherExceptionMask flag tells NSExceptionHandler to capture "everything below it" on the exception-handler chain. You can remove that flag, but then HIToolKit will probably get any IBAction exceptions again, since it appears to be lower on said chain.
Apple's docs have info about the flags: NSExceptionHandler docs
So when an NSException is raised (anywhere in your app AFAIK), and NSLogAndHandleEveryExceptionMask is set, these are called in the delegate in-order:
-exceptionHandler:shouldLogException:mask: is called first.
-exceptionHandler:shouldHandleException:mask: is called second.
Just put your "crash code" inside the second delegate method and you should be OK.
Helpful article: Understanding Exceptions and Handlers in Cocoa
The reason I think you were having trouble getting NSExceptionHandler's delegate to work is because it's NOT compatible with a custom method set with NSSetUncaughtExceptionHandler(), which was part of the answer in my previous question. Per Apple:
The NSExceptionHandler class provides
facilities for monitoring and
debugging exceptional conditions in
Objective-C programs. It works by
installing a special uncaught
exception handler via the
NSSetUncaughtExceptionHandler
function. Consequently, to use the
services of NSExceptionHandler, you
must not install your own custom
uncaught exception handler.
It also probably doesn't work well when you override NSApplication's -reportException: method.
Lastly, there doesn't appear to be a need to use #catch/#try/#finally (also part of my previous answer). Configuring NSExceptionHandler inside +initialize appears to "kick in" immediately, unlike overriding NSApplication's -reportException: method.
You can’t reliably. Throwing exceptions across API boundaries is not supported unless explicitly documented (and I can’t think of any cases that are explicitly documented).
I'm trying to get data from a website- xml. Everything works fine.
But the UIButton remains pressed until the xml data is returned and thus if theres a problem with the internet service, it can't be corrected and the app is virtually unusable.
here are the calls:
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if(!appDelegate.XMLdataArray.count > 0){
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[appDelegate GetApps]; //function that retrieves data from Website and puts into the array - XMLdataArray.
}
XMLViewController *controller = [[XMLViewController alloc] initWithNibName:#"MedGearsApps" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
It works fine, but how can I make the view buttons functional with getting stuck. In other words, I just want the UIButton and other UIButtons to be functional whiles the thing works in the background.
I heard about performSelectorInMainThread but I can't put it to practice correctly.
You don’t understand the threading model much and you’re probably going to shoot yourself in the foot if you start adding asynchronous code without really understanding what’s going on.
The code you wrote runs in the main application thread. But when you think about it, you don’t have to write no main function — you just implement the application delegate and the event callbacks (such as touch handlers) and somehow they run automatically when the time comes. This is not a magic, this is simply a Cocoa object called a Run Loop.
Run Loop is an object that receives all events, processes timers (as in NSTimer) and runs your code. Which means that when you, for example, do something when the user taps a button, the call tree looks a bit like this:
main thread running
main run loop
// fire timers
// receive events — aha, here we have an event, let’s call the handler
view::touchesBegan…
// use tapped some button, let’s fire the callback
someButton::touchUpInside
yourCode
Now yourCode does what you want to do and the Run Loop continues running. But when your code takes too long to finish, such as in your case, the Run Loop has to wait and therefore the events will not get processed until your code finishes. This is what you see in your application.
To solve the situation you have to run the long operation in another thread. This is not very hard, but you’ll have to think of a few potential problems nevertheless. Running in another thread can be as easy as calling performSelectorInBackground:
[appDelegate performSelectorInBackground:#selector(GetApps) withObject:nil];
And now you have to think of a way to tell the application the data has been loaded, such as using a notification or calling a selector on the main thread. By the way: storing the data in the application delegate (or even using the application delegate for loading the data) is not very elegant solution, but that’s another story.
If you do choose the performSelectorInBackground solution, take a look at a related question about memory management in secondary threads. You’ll need your own autorelease pool so that you won’t leak autoreleased objects.
Updating the answer after some time – nowadays it’s usually best to run the code in background using Grand Central Dispatch:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// No explicit autorelease pool needed here.
// The code runs in background, not strangling
// the main run loop.
[self doSomeLongOperation];
dispatch_sync(dispatch_get_main_queue(), ^{
// This will be called on the main thread, so that
// you can update the UI, for example.
[self longOperationDone];
});
});
Use NSURLConnection's connectionWithRequest:delegate: method. This will cause the specified request to be sent asynchronously. The delegate should respond to connection:didReceiveResponse: and will be sent that message once the response is completely received.
You can make use of a background operation that gets pushed into the operation queue:
BGOperation *op = [[BGOperation alloc] init];
[[self operationQueue] addOperation:op];
[op release];
I've created specific "commands" that get executed in the background:
#implementation BGOperation
# pragma mark Memory Management
- (BGOperation *)init
{
if ((self = [super init]) != nil)
/* nothing */;
return self;
}
- (void)dealloc
{
self.jobId = nil;
[super dealloc];
}
# pragma mark -
# pragma mark Background Operation
- (void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[appDelegate GetApps];
[pool release];
return;
}
#end
After completion it might be a good idea to send a notification to the main thread because the internal database has been changed.
It looks as if you might be using NSURLConnection inside your getApps method. If so, you should convert it to an asynchronous call.