FMDatabase locked, best practice for usage within class

浪尽此生 提交于 2019-12-03 21:59:24

You should also consider using FMDatabaseQueue, and stick that in some shared area of your code. It'll be safe to use it on multiple threads as well.

I don't agree with Tumtum's answer. Please read document from FMDB:

Using a single instance of FMDatabase from multiple threads at once is a bad idea. It has always been OK to make a FMDatabase object per thread. Just don't share a single instance across threads, and definitely not across multiple threads at the same time. Bad things will eventually happen and you'll eventually get something to crash, or maybe get an exception, or maybe meteorites will fall out of the sky and hit your Mac Pro. This would suck.

So don't instantiate a single FMDatabase object and use it across multiple threads.

Instead, use FMDatabaseQueue. Instantiate a single FMDatabaseQueue and use it across multiple threads. The FMDatabaseQueue object will synchronize and coordinate access across the multiple threads.

I've implemented a singelton with an FMDatabase using the following code, which seems to have resolved the issue with the locked Database error. Not sure if this is good practice, but here's the code for everyone that needs a similar implementation. I used this tutorial on singeletons.

The singleton:

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

Using the singelton

Then everywhere I need to acces the database I use:

DatabaseController* sharedDatabase = [DatabaseController sharedDatabase];
[sharedDatabase initDB]; //only the first time! In my case in my app delegate
FMDatabase *db = [sharedDatabase getDB];
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!