FMDatabase已锁定,是课堂内使用的最佳实践

时间:2014-05-01 09:26:15

标签: ios objective-c singleton fmdb

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

现在我想在songleton中添加数据库连接,并想知道这是不是很好的做法以及如何将FMDatabase包装在单例类中。任何有关问题发生原因以及解决此问题的最佳方法的帮助都将非常感谢!

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

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

  

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

     

因此,不要实例化单个FMDatabase对象并在多个线程中使用它。

     

相反,请使用FMDatabaseQueue。实例化单个FMDatabaseQueue并在多个线程中使用它。 FMDatabaseQueue对象将同步并协调跨多个线程的访问。

答案 2 :(得分:0)

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

单身人士:

DatabaseController.h:

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

@interface DatabaseController : NSObject

@property (strong, nonatomic) FMDatabase *db;

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

@end

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

使用singelton

然后我需要访问我使用的数据库:

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