0

I wonder how run sqlite querys in the background as suggested in What are best practices that you use when writing Objective-C and Cocoa?

This is my try:

- (void) run:(NSString *)sql {
NSArray *data = [NSArray arrayWithObjects:
                 sql,
                 [self returnItemClass],
                 nil];

NSInvocationOperation *operation = 
[[NSInvocationOperation alloc] initWithTarget:self 
                                     selector:@selector(loadRecords:) 
                                       object:data]; 
[self.queue addOperation:operation];
[operation release];
}

- (void) loadRecords:(NSArray *)data {
    NSLog(@"Runing sql");
    NSString * sql = [data objectAtIndex:0];
    Class cls = [data objectAtIndex:1];

    Db *db= [Db currentDb];

    NSArray *list = [db loadAndFill:sql theClass:cls];
    [UIAppDelegate performSelectorOnMainThread:@selector(recordsLoaded:)
                                           withObject:list
                                        waitUntilDone:YES];
}

- (void) recordsLoaded:(NSArray *)data {
NSLog(@"Loading sql");
for (DbObject *o in data) {
    [self.objectCache setObject:o forKey:[NSNumber numberWithInt:o.Id]];
}
}

However, I get a error when try to run

    [UIAppDelegate performSelectorOnMainThread:@selector(recordsLoaded:)
                                       withObject:list
                                    waitUntilDone:YES];

And complain about not can send a message to recordsLoaded.

This is the correct aproach?

And how fill the data in the UITableView?

Community
  • 1
  • 1
mamcx
  • 15,916
  • 26
  • 101
  • 189

4 Answers4

2

There isn't too much in the way of multi-threading iPhone applications, as a result of Apple's wishes and design.

The generic messaging philosophy, however, is conducive to writing event-oriented programs. Your program should aim to respond to user input and environmental variables by setting the delegates to event dispatchers.

SQLite3, as a database, is not a server in the classical sense. Its protocol for interacting with a specific file format, whose libraries can be directly included in source code. In other words, when you need to get data from the database, you should be able to read it immediately. You shouldn't, and can't, spawn a SQLite3 process to work in the background.

Regarding your stated syntax, why are you using performSelectorOnMainThread... instead of just calling

[UIAppDelegate recordsLoaded:list]

?

Willi Ballenthin
  • 6,444
  • 6
  • 38
  • 52
  • Presumably that's because the recordsLoaded: method updates the UI, and you should only update the UI from the main thread. Since this code is going to be running on a background thread, you need to use performSelectorOnMainThread. – Daniel Dickison Apr 09 '09 at 21:20
0

I've tried these two solutions and they worked perfectly. You can either use critical sections or NSOperationQueue and I prefer the first one, here is the code for both of them:

define some class "DatabaseController" and add this code to its implementation:

static NSString * DatabaseLock = nil;
+ (void)initialize {
    [super initialize];
    DatabaseLock = [[NSString alloc] initWithString:@"Database-Lock"];
}
+ (NSString *)databaseLock {
    return DatabaseLock;
}

- (void)writeToDatabase1 {
    @synchronized ([DatabaseController databaseLock]) {
        // Code that writes to an sqlite3 database goes here...
    }
}
- (void)writeToDatabase2 {
    @synchronized ([DatabaseController databaseLock]) {
        // Code that writes to an sqlite3 database goes here...
    }
}

OR to use the NSOperationQueue you can use:

static NSOperationQueue * DatabaseQueue = nil;
+ (void)initialize {
    [super initialize];

    DatabaseQueue = [[NSOperationQueue alloc] init];
    [DatabaseQueue setMaxConcurrentOperationCount:1];
}
+ (NSOperationQueue *)databaseQueue {
    return DatabaseQueue;
}

- (void)writeToDatabase {
    NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(FUNCTION_THAT_WRITES_TO_DATABASE) object:nil];
    [operation setQueuePriority:NSOperationQueuePriorityHigh];
    [[DatabaseController databaseQueue] addOperations:[NSArray arrayWithObject:operation] waitUntilFinished:YES];
    [operation release];
}

these two solutions block the current thread until the writing to database is finished which you may consider in most of the cases.

Mousa
  • 2,926
  • 1
  • 27
  • 35
0
[UIAppDelegate performSelectorOnMainThread:@selector(recordsLoaded:)
                                withObject:list
                             waitUntilDone:YES];

should be

[self performSelectorOnMainThread:@selector(recordsLoaded:)
                       withObject:list
                    waitUntilDone:YES];

unless you've defined UIAppDelegate as something like

#define UIAppDelegate ([UIApplication sharedApplication].delegate)
Daniel Dickison
  • 21,832
  • 13
  • 69
  • 89
-3

The problem there is that the iPhone really doesn't have a way to readily run programs in the background.

Charlie Martin
  • 110,348
  • 25
  • 193
  • 263