FMDatabaseQueue 如何返回一个值

Posted

技术标签:

【中文标题】FMDatabaseQueue 如何返回一个值【英文标题】:FMDatabaseQueue How To Return A Value 【发布时间】:2013-05-27 03:17:44 【问题描述】:

我在我的 ios 应用程序中使用 FMDatabaseQueue。我一直在理解如何在创建队列时返回值。感谢您的帮助!

 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
];

【问题讨论】:

你想退回什么?来自 db 的单个值或一组(或数组)结果(这看起来像是你试图通过“select *”在那里做的)??? 【参考方案1】:

这取决于您要返回的内容。但是你可能会感到困惑的是,如果你从 inDatabase 块内发出 return 语句,你是从块返回,而不是从包含这个 inDatabase 块的方法返回。

因此,您根本不从 inDatabase 块返回值,而是从块外部返回值。所以你通常会做的是,你会声明你的变量要在inDatabase块之外返回,你的inDatabase块会更新它,然后,当块完成时,那是您返回结果的时候(不是来自inDatabase 块)。

一个常见的例子是如果你正在构建一个NSMutableArray:所以在块之外创建可变数组,然后从块内添加值,然后返回结果之后你退出inDatabase 块:

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

或者,如果您正在处理一些基本类型,例如 NSIntegerBOOL 或者您有什么,您可以使用 __block 限定符声明变量。例如,我将使用它来返回一个 BOOL 成功变量,例如:

__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

虽然您第一次遇到它时可能会感到困惑,但理解这一点很有用。当你开始大量使用 GCD 块时,这种模式很常见。当您有一个块(由^ 字符表示)时,您几乎必须将其视为您在 inside 的 main 方法中定义的一个函数。当您在块内遇到return 时,您将返回到包含该块的方法。

方块介绍见:

A Short Practical Guide to Blocks

Blocks Programming Topics

【讨论】:

在我的例子中,要在swift中返回一个Bool,它首先返回,然后块内的函数(闭包)稍后执行。所以它总是返回false。有什么想法吗? 使用 FMDB? inDatabase 方法同步运行。唯一一次遇到您描述的问题是当您异步执行某些操作时。如果您正在异步执行某些操作,您将 (a) 不会尝试立即返回值;而是 (b) 实现您自己的完成处理程序模式(例如,如 this asynchronous networking question 中所示)。但是除非异步调度某些东西,否则您不需要处理它。 非常感谢,我想这解释了为什么我在 inDatabase 或 inTransaction 块中遇到数据库锁定问题,如果我在执行另一个数据库查询或更新的地方阻止了某些东西。【参考方案2】:

您可以在块方法中传递其他内容,如下例所示

如果您阅读 FMDB's Github Page 页面,您可能会了解事情的运作方式

[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.

【讨论】:

我知道我在问一个有点奇怪的问题,但是你从哪里得到这个“whoopsSomethingWrongHappened”。你有没有在某个地方早些时候宣布过?非常感谢您的帮助。

以上是关于FMDatabaseQueue 如何返回一个值的主要内容,如果未能解决你的问题,请参考以下文章

_fmdatabaseQueue 可以从其他类访问吗

使用 Swift 的 FMDatabaseQueue (FMDB)

FMDatabaseQueue 没有在类 init() 中幸存

执行 [FMDatabaseQueue inDatabase:] 后出现错误,因为周围至少有一个打开的结果集?

如何在 cassandra 中捕获条件插入的返回值?

使用多线程读取 FMDB 数据库