1. 设计数据库管理类
- 单例模式:创建一个数据库管理类,采用单例模式确保整个应用程序只有一个数据库实例,便于统一管理。例如:
@interface DatabaseManager : NSObject
@property (nonatomic, strong) FMDatabase *database;
+ (instancetype)sharedManager;
@end
@implementation DatabaseManager
static DatabaseManager *sharedInstance = nil;
+ (instancetype)sharedManager {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:@"your_database_name.db"];
sharedInstance.database = [FMDatabase databaseWithPath:databasePath];
if (![sharedInstance.database open]) {
NSLog(@"Error opening database: %@", sharedInstance.database.lastError);
}
});
return sharedInstance;
}
@end
- 线程安全的方法:在数据库管理类中,所有对数据库的操作方法都应该是线程安全的。比如查询、插入、更新和删除操作。
2. 锁机制
- 选择合适的锁:
pthread_mutex
:这是一种简单且高效的C语言级别的互斥锁。可以通过pthread_mutex_init
初始化,pthread_mutex_lock
锁定,pthread_mutex_unlock
解锁。例如:
pthread_mutex_t databaseMutex;
pthread_mutex_init(&databaseMutex, NULL);
// 在数据库操作前
pthread_mutex_lock(&databaseMutex);
// 执行数据库操作,如插入数据
[database executeUpdate:@"INSERT INTO your_table (column1, column2) VALUES (?,?)", value1, value2];
// 操作完成后
pthread_mutex_unlock(&databaseMutex);
- **`dispatch_semaphore`**:基于GCD的信号量机制,在iOS开发中使用方便。例如:
dispatch_semaphore_t databaseSemaphore = dispatch_semaphore_create(1);
dispatch_semaphore_wait(databaseSemaphore, DISPATCH_TIME_FOREVER);
// 执行数据库操作
[database executeQuery:@"SELECT * FROM your_table"];
dispatch_semaphore_signal(databaseSemaphore);
- 锁的应用范围:将锁应用在对数据库进行读写操作的代码段,确保同一时间只有一个线程能够访问数据库。
3. 避免数据竞争和死锁
- 数据竞争:
- 统一访问入口:通过前面提到的单例数据库管理类,确保所有对数据库的访问都经过该类的方法,在方法内部加锁,避免多个线程直接操作数据库导致数据竞争。
- 合理安排操作顺序:对于涉及多个数据库操作的事务,按照固定的顺序进行操作,避免不同线程以不同顺序操作数据库导致数据竞争。
- 死锁:
- 避免嵌套锁:尽量避免在持有一个锁的情况下尝试获取另一个锁,特别是在多个线程中以不同顺序获取锁的情况。如果确实需要嵌套锁,要确保所有线程获取锁的顺序一致。
- 设置超时:在获取锁时设置超时时间,例如使用
dispatch_semaphore_wait
时,可以设置一个DISPATCH_TIME_NOW
加上一个时间间隔,如果在规定时间内未能获取锁,则放弃操作并处理相应的错误,防止线程无限期等待。例如:
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC));
long result = dispatch_semaphore_wait(databaseSemaphore, timeout);
if (result == 0) {
// 获取锁成功,执行数据库操作
[database executeUpdate:@"UPDATE your_table SET column1 =? WHERE id =?", value, idValue];
dispatch_semaphore_signal(databaseSemaphore);
} else {
// 获取锁超时,处理错误
NSLog(@"Failed to acquire semaphore within timeout");
}
4. 数据库操作类
- 封装数据库操作:创建具体的数据库操作类,例如
UserDatabaseOperation
,将对用户表的操作封装在该类中。每个操作方法内部调用数据库管理类的方法,并在合适的位置加锁。
@interface UserDatabaseOperation : NSObject
+ (BOOL)insertUser:(User *)user;
+ (User *)getUserById:(NSInteger)userId;
@end
@implementation UserDatabaseOperation
+ (BOOL)insertUser:(User *)user {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
BOOL success = [[DatabaseManager sharedManager].database executeUpdate:@"INSERT INTO users (name, age) VALUES (?,?)", user.name, @(user.age)];
dispatch_semaphore_signal(semaphore);
return success;
}
+ (User *)getUserById:(NSInteger)userId {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
FMResultSet *resultSet = [[DatabaseManager sharedManager].database executeQuery:@"SELECT name, age FROM users WHERE id =?", @(userId)];
User *user = nil;
if ([resultSet next]) {
user = [[User alloc] init];
user.name = [resultSet stringForColumn:@"name"];
user.age = [resultSet intForColumn:@"age"];
}
[resultSet close];
dispatch_semaphore_signal(semaphore);
return user;
}
@end
5. 数据模型类
- 定义数据模型:创建与数据库表结构对应的模型类,例如
User
类,用于封装从数据库中读取的数据或准备插入数据库的数据。
@interface User : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@implementation User
@end