119

I have a small sqlitedb in my iOS device. When a user presses a button, I fetch the data from sqlite & show it to user.

This fetching part I want to do it in a background thread (to not block the UI main thread). I do this like so -

[self performSelectorInBackground:@selector(getResultSetFromDB:) withObject:docids];

After the fetching & a little bit of processing, I need to update the UI. But since (as a good practice) we should not perform UI updation from background threads. I call a selector on mainthread like so -

[self performSelectorOnMainThread:@selector(showResults) withObject:nil waitUntilDone:NO];

But my App crashes in the first step. i.e. starting a background thread. Is this not a way to start background threads in iOS?

UPDATE 1: After [self performSelectorInBackground.... I get this stacktrace, no info what so ever -

enter image description here

UPDATE 2: I even tried, starting a background thread like so - [NSThread detachNewThreadSelector:@selector(getResultSetFromDB:) toTarget:self withObject:docids]; but still I get same stacktrace.

Just so that I clarify, when I perform this operation on main thread everything runs smooth...

UPDATE 3 This is the method I am trying to run from background

- (void)getResultSetFromDB:(NSMutableArray *)toProceessDocids
{
    SpotMain *mirror = [[SpotMain alloc] init];
    NSMutableArray *filteredDocids = toProceessDocids;

    if(![gMediaBucket isEqualToString:@""])
        filteredDocids = [mirror FetchDocIdsForMediaBucketWithDocID:filteredDocids mBucket:gMediaBucket numRes:-1];
    if(![gMediaType isEqualToString:@""])
        filteredDocids = [mirror FetchDocIdsForMediaType:filteredDocids mediaType:gMediaType numRes:-1];
    if(![gPlatform isEqualToString:@""])
        filteredDocids = [mirror FetchDocIdsForPlatformID:filteredDocids platformId:@"1" numRes:-1];

    self.resultSet = [mirror FetchObjectFromDocid:filteredDocids];
    [filteredDocids release];
    [mirror release];

    [self performSelectorOnMainThread:@selector(showResults) withObject:nil waitUntilDone:NO];
    return;
}
Srikar Appalaraju
  • 71,928
  • 54
  • 216
  • 264

5 Answers5

272

If you use performSelectorInBackground:withObject: to spawn a new thread, then the performed selector is responsible for setting up the new thread's autorelease pool, run loop and other configuration details – see "Using NSObject to Spawn a Thread" in Apple's Threading Programming Guide.

You'd probably be better off using Grand Central Dispatch, though:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self getResultSetFromDB:docids];
});

GCD is a newer technology, and is more efficient in terms of memory overhead and lines of code.


Updated with a hat tip to Chris Nolet, who suggested a change that makes the above code simpler and keeps up with Apple's latest GCD code examples.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Scott Forbes
  • 7,397
  • 1
  • 26
  • 39
  • cool! didn't know this. Does this apply to `[NSThread detachNewThreadSelector:@selector....` also? – Srikar Appalaraju Aug 14 '11 at 07:25
  • Yes. Per the Apple docs, calling `performSelectorInBackground:withObject:` "is the same as if you called the `detachNewThreadSelector:toTarget:withObject:` method of `NSThread` with the current object, selector, and parameter object as parameters." – Scott Forbes Aug 14 '11 at 07:29
  • Is there a difference between `(unsigned long)NULL` and `0` in this matter? – Sti Mar 09 '14 at 01:12
  • 4
    @Sti from apple Dev [Docs](https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1): Note: The second argument to the dispatch_get_global_queue function is reserved for future expansion. For now, you should always pass 0 for this argument. – Jawad Al Shaikh May 07 '14 at 13:30
  • Should I then use performSelectorOnMainThread to update UI with operation results or there is more consistent way to update UI with GCD? – Ilya Denisov Feb 03 '15 at 10:53
  • Also, you can change the flag from *DISPATCH_QUEUE_PRIORITY_DEFAULT* to *DISPATCH_QUEUE_PRIORITY_BACKGROUND* – OhadM Jul 21 '15 at 11:50
11

Well that's pretty easy actually with GCD. A typical workflow would be something like this:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
    dispatch_async(queue, ^{
        // Perform async operation
        // Call your method/function here
        // Example:
        // NSString *result = [anObject calculateSomething];
                dispatch_sync(dispatch_get_main_queue(), ^{
                    // Update UI
                    // Example:
                    // self.myLabel.text = result;
                });
    });

For more on GCD you can take a look into Apple's documentation here

Pawan Ahire
  • 199
  • 1
  • 10
4

Enable NSZombieEnabled to know which object is being released and then accessed. Then check if the getResultSetFromDB: has anything to do with that. Also check if docids has anything inside and if it is being retained.

This way you can be sure there is nothing wrong.

Community
  • 1
  • 1
Nicolas S
  • 5,325
  • 3
  • 29
  • 36
  • Please copy the line you used that run smoothly on main thread. – Nicolas S Aug 14 '11 at 07:06
  • I use this from main thread & atleast it hits that method instead of abruptly crashing - `[self getResultSetFromDB:docids];` – Srikar Appalaraju Aug 14 '11 at 07:11
  • have you enabled what i told you? – Nicolas S Aug 14 '11 at 07:12
  • Put a breakpoint in this line: _SpotMain *mirror = [[SpotMain alloc] init];_ and tell me if its hit and, if tehn, which line crashes. Enable zombies please so we can get a clear error log. – Nicolas S Aug 14 '11 at 07:16
  • yes, I have enabled zombies. I get this - ` 2011-08-14 12:49:42.697 FLO[16211:707] *** -[FMResultSet release]: message sent to deallocated instance 0x2bff80 2011-08-14 12:49:42.697 FLO[16211:1607] *** __NSAutoreleaseNoPool(): Object 0x2c0cc0 of class __NSCFData autoreleased with no pool in place - just leaking`. Also when I try to call this method from background thread I do not reach `SpotMain *mirror...`, It crashes soon after entering the background thread... – Srikar Appalaraju Aug 14 '11 at 07:20
  • This is not a zombie-related problem. The problem is that, if you create a new thread using `performSelectorInBackground:withObject`, your new thread doesn't have an autorelease pool (unless you create one manually). Hence the error message about autoreleasing an object with no pool in place. – Scott Forbes Aug 14 '11 at 07:27
2

The default sqlite library that comes with iOS is not compiled using the SQLITE_THREADSAFE macro on. This could be a reason why your code crashes.

Mugunth
  • 14,461
  • 15
  • 66
  • 94
2

Swift 2.x answer:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        self.getResultSetFromDB(docids)
    }
Crashalot
  • 33,605
  • 61
  • 269
  • 439