1

I want to start using FMDatabaseQueue to run queries in different threads within my application simultaneously, but I'm not sure how to implement a read action within a method. I'm using the example found here: http://ccgus.github.io/fmdb/html/Classes/FMDatabaseQueue.html

How can I implement this snippet within a method so it runs synchronous and returns the results?

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];

[queue inDatabase:^(FMDatabase *db) {
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];

    FMResultSet *rs = [db executeQuery:@"select * from foo"];
    while ([rs next]) {
        //…
    }
}];

Edit: to clarify, I want to either return the resultset or an NSArray I fill myself

Tum
  • 6,937
  • 2
  • 25
  • 23
  • 1
    BTW, it might be prudent to check the return value of the `executeUpdate` statements, and make sure it's not returning `FALSE` (and if it does, look at `[db lastErrorMessage]` to identify what the error was, if anything). Also, when performing multiple `executeUpdate` statements, in might be worth doing `inTransaction` rather than `inDatabase`. The difference might not be observable if only doing three update statements, but it's a little more efficient, and makes a big difference if doing many updates. – Rob Jun 05 '14 at 21:48
  • 1
    Also, you are using a local variable for `FMDatabaseQueue`. Obviously, you want to use the same `FMDatabaseQueue` instance for all threads (or else you don't enjoy the synchronization that `FMDatabaseQueue` provides). I assume you were just trying to keep your example simple, but I mention it in case you were really using local variables for the queue. – Rob Jun 05 '14 at 23:13
  • Thanks for the additional information. I was thinking to put the queue in a singleton. – Tum Jun 06 '14 at 07:58
  • Yep, that's a common pattern. – Rob Jun 06 '14 at 11:51

1 Answers1

2

FMDatabaseQueue already runs this synchronously. In terms of returning the results, just create an array object and then return the results:

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
NSMutableArray *results = [NSMutableArray array];

[queue inDatabase:^(FMDatabase *db) {
    if (![db executeUpdate:@"INSERT INTO myTable VALUES (?)", @(1)])
        NSLog(@"%s: executeUpdate 1 error: %@", __PRETTY_FUNCTION__, [db lastErrorMessage]);
    if (![db executeUpdate:@"INSERT INTO myTable VALUES (?)", @(2)])
        NSLog(@"%s: executeUpdate 2 error: %@", __PRETTY_FUNCTION__, [db lastErrorMessage]);
    if (![db executeUpdate:@"INSERT INTO myTable VALUES (?)", @(3)])
        NSLog(@"%s: executeUpdate 3 error: %@", __PRETTY_FUNCTION__, [db lastErrorMessage]);

    FMResultSet *rs = [db executeQuery:@"select * from foo"];
    while ([rs next]) {
        // just add your objects to the `results` array
    }
    [rs close];
}];

return results;
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I already tried something similar but it returned an empty array, so I figured it was due to the queue being asynchronous. Perhaps I declared the array that was to be returned inside the queue. Thanks for the clear example! – Tum Jun 06 '14 at 07:56
  • @Tumtum Yeah, I suspected as much, which is why I suggested adding the error logging in the SQL (in case there was a database issue, e.g. missing table, if it had more than one column, etc.). It could also be a failure to instantiate the mutable array. It's hard to tell on the basis of your original code snippet. But hopefully this has you off to the races. – Rob Jun 06 '14 at 11:52
  • I think you need to define the array as `__block NSMutableArray *results = [NSMutableArray array];` in order for it to be accessible within the block. – Chris Nov 25 '14 at 01:10
  • @Chris No, you use the `__block` qualifier if you're changing the `results` pointer, itself, but not if you're just mutating an existing object that was instantiate before the block. – Rob Nov 25 '14 at 01:25
  • If you're changing the value of `result` and then want to access that new value after the block, you don't need `__block` ? How is this different than this example: http://stackoverflow.com/questions/7080927/what-does-the-block-keyword-mean – Chris Nov 25 '14 at 02:29
  • You'll notice that the examples in that question (and the docs) they are using a fundamental data type, `int`. You need `__block` in that case because you're actually changing the value of that variable. But in this case, we're not technically changing the value of `results` (it's pointing to the exact same object the whole time), but rather we're just calling that object's methods (e.g. `addObject`). See http://stackoverflow.com/q/20915383/1271826. – Rob Nov 25 '14 at 14:07