FMDatabase 锁定,类内使用的最佳实践

Posted

技术标签:

【中文标题】FMDatabase 锁定,类内使用的最佳实践【英文标题】:FMDatabase locked, best practice for usage within class 【发布时间】:2014-05-01 09:26:14 【问题描述】:

我有一个同步方法,用于我正在构建的应用程序,使用 FMDatabase 包装器将数据存储在本地 SQLite 中。当我将所有查询放在一个类中时,一切正常。然而,为了保持复杂性,我为部分同步添加了一些数据控制器类,但是当我这样做时,FMDatabase 会给出“数据库锁定”错误,无论是当我在数据类中添加新连接时,还是当我将初始数据库连接发送为一个参数。

现在我正在考虑在单例中添加数据库连接,并且想知道这是否是一种好习惯以及我如何将 FMDatabase 包装在单例类中。任何有关问题发生原因以及解决此问题的最佳方法的帮助将不胜感激!

【问题讨论】:

【参考方案1】:

您还应该考虑使用 FMDatabaseQueue,并将其粘贴在代码的某个共享区域中。在多个线程上使用它也是安全的。

【讨论】:

啊,谢谢,我会调查的。旁注:SQlite / FMDB 可以在队列或事务中处理对 lastInsertRowId 的调用,还是我应该解决这个问题?【参考方案2】:

我不同意 Tumtum 的回答。 请阅读 FMDB 中的document:

同时使用来自多个线程的单个 FMDatabase 实例是一个坏主意。每个线程都可以创建一个 FMDatabase 对象。只是不要跨线程共享单个实例,绝对不要同时跨多个线程。坏事最终会发生,你最终会导致崩溃,或者可能会出现异常,或者陨石可能会从天而降并击中你的 Mac Pro。这会很糟糕。

所以不要实例化单个 FMDatabase 对象并跨多个线程使用它。

改为使用 FMDatabaseQueue。实例化单个 FMDatabaseQueue 并跨多个线程使用它。 FMDatabaseQueue 对象将跨多个线程同步和协调访问。

【讨论】:

【参考方案3】:

我已经使用以下代码实现了一个带有 FMDatabase 的单例,这似乎解决了锁定数据库错误的问题。不确定这是否是一种好的做法,但这里是需要类似实现的每个人的代码。我在单子上使用了this tutorial。

单身人士:

数据库控制器.h:

#import <Foundation/Foundation.h>
#import "FMDatabase.h"

@interface DatabaseController : NSObject

@property (strong, nonatomic) FMDatabase *db;

+ (id)sharedDatabase;
- (void)initDB;
- (FMDatabase *)getDB;

@end

数据库控制器.m:

#import "DatabaseController.h"

@implementation DatabaseController

static DatabaseController *sharedDatabase = nil;

// Get the shared instance and create it if necessary.
+ (DatabaseController *)sharedDatabase 
    if (sharedDatabase == nil) 
        sharedDatabase = [[super allocWithZone:NULL] init];
    

    return sharedDatabase;


- (void)initDB 
    NSString *databaseName = @"MyDatabase.db";
    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentDir = [documentPaths objectAtIndex:0];
    NSString *databasePath = [documentDir stringByAppendingPathComponent:databaseName];

    _db = [FMDatabase databaseWithPath:databasePath];

    if (![_db open])
        NSLog(@"Database problem");


// Return database instance
- (FMDatabase *)getDB 
    return _db;


// We can still have a regular init method, that will get called the first time the Singleton is used.
- (id)init 
    self = [super init];

    if (self) 
        // Work your initialising magic here as you normally would
    

    return self;


// Equally, we don't want to generate multiple copies of the singleton.
- (id)copyWithZone:(NSZone *)zone 
    return self;


@end

使用单例

然后在我需要访问我使用的数据库的任何地方:

DatabaseController* sharedDatabase = [DatabaseController sharedDatabase];
[sharedDatabase initDB]; //only the first time! In my case in my app delegate
FMDatabase *db = [sharedDatabase getDB];

【讨论】:

以上是关于FMDatabase 锁定,类内使用的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

在 Android 中发出异步 HTTP 请求是不是有公认的最佳实践?

乐观锁定的 JPA 版本字段的最佳类型

在使用表时更新表中数据而不锁定表的最佳方法是啥?

在 asp.net 中锁定缓存的最佳方法是啥?

如何查看以下 FMDatabase executeUpdate 语句是不是成功?

Helm(三)—最佳实践