FMDatabaseQueue How To Return A Value
Asked Answered
S

2

8

I'm using FMDatabaseQueue in my iOS application. I'm stuck in understanding how to return the value upon creating the queue. Appreciate your help!!

 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]) {
    ... 
}
// I want value to be returned from here
}];
Steward answered 27/5, 2013 at 3:17 Comment(1)
what do you want returned? A single value, or a set (or array) of results from the db (which is what it looks like you're trying to do via "select *" up there)???Complacency
D
15

It depends upon what you're trying to return. But what might be confusing you is that if you issue a return statement from inside the inDatabase block, you're returning from the block, you're not returning from the method that contains this inDatabase block.

So, you simply don't return values from the inDatabase block, but rather you return values from outside the block. So what you'll commonly do, is you'll declare your variable to be returned outside the inDatabase block, your inDatabase block will update it, and then, when the block is done, that's when you return the results (not from within the inDatabase block).

A common example is if you're building an NSMutableArray: So create the mutable array outside of the block, and then add values from within the block, but then return the results after you exit the inDatabase block:

NSMutableArray *results = [NSMutableArray array];   // declare this outside the block

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath]; 

[queue inDatabase:^(FMDatabase *db) {

    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @(1)];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @(2)];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @(3)];

    FMResultSet *rs = [db executeQuery:@"select * from foo"];
    while ([rs next]) {
        ... 
        [results addObject:result];                 // add values inside the block
    }
    [rs close];
}];

return results;                                     // return the results outside the block

Or, if you're dealing with some fundamental type, like a NSInteger or BOOL or what have you, you'd declare the variable with a __block qualifier. For example, I'll use this to return a BOOL success variable, e.g.:

__block BOOL success;                               // again, define outside the block

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

[queue inDatabase:^(FMDatabase *db) {

    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @(1)];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @(2)];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @(3)];

    FMResultSet *rs = [db executeQuery:@"select * from foo"];
    if (!rs)
    {
        NSLog(@"%s: %@", __FUNCTION__, [db lastErrorMessage]);
        success = NO;     // set the value inside the block
        return;           // note, this doesn't exit the method; this exits this `inDatabase` block
    }

    while ([rs next]) {
        ... 
    }

    [rs close];
    success = YES;        // another example of setting that `success` variable
}];

// so whether I successfully completed the block, or whether I hit the `return` 
// statement inside the block, I'll fall back here, at which point I'll return my
// boolean `success` variable

return success;           // don't return the value until after you exit the block

While this might seem confusing the first time you encounter it, it's useful to understand this. When you start using GCD block a lot, this pattern is very common. When you have a block (indicated by the ^ character), you almost have to think of it as an function that you're defining inside your main method. When you encounter a return inside a block, you're returning to the method that contains the block.

For introductions to blocks see:

Deign answered 27/5, 2013 at 6:23 Comment(3)
In my case to return a Bool in swift,it returns first,then the function inside block (closure) execute later. So it always return false. Any idea?Vorster
With FMDB? The inDatabase method runs synchronously. The only time you have the problem you describe is when you're doing something asynchronously. If you were doing something asynchronously, you would (a) not attempt to return value immediately; but rather (b) implement your own completion handler pattern (e.g. like shown in this asynchronous networking question). But you don't need to deal with that unless dispatching something asynchronously.Deign
thanks a lot, I think this explains why I had my database lock problem, within inDatabase or inTransaction blocks, if I block something out where I execute another database query or update.Katt
C
0

You can pass another things in block method like the example below

if you read the FMDB's Github Page page you might understand how things work

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    [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]];

    if (whoopsSomethingWrongHappened) {
        *rollback = YES;
        return;
    }
    // etc…
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]];
}]; 






 FMDatabaseQueue will run the blocks on a serialized queue 
(hence the name of the class). So if you call FMDatabaseQueue's 
methods from multiple threads at the same time, they 
will be executed in the order they are received. 
This way queries and updates won't step on each other's toes,
 and every one is happy.

    Note: The calls to FMDatabaseQueue's methods are blocking. So even though you are passing along blocks, they will not be run on another thread.
Cubic answered 27/5, 2013 at 4:22 Comment(1)
I know I am asking a little weird question, but from where you get this "whoopsSomethingWrongHappened". Have you declared it earlier somewhere ? help will be really appreciated.Malraux

© 2022 - 2024 — McMap. All rights reserved.