iOS中FMDB的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/aaaaazq/article/details/83961538

在iOS开发中,如果本地需要做大量的缓存,FMDB用的是比较多的,怎么集成到项目中我这里就不说了,主要说一下FMDB使用的一个思路和自己遇到的坑。
在我自己开发聊天系统的时候,我一般是用FMDB用来缓存最近联系人列表和用户的聊天消息列表。因为聊天系统,涉及到的读和写比较频繁,所以我会封装一个单例类用来处理每个部分的数据存储和读写。
下面是用来管理最近联系人缓存的功能实现,其余的模块也可以按照这样的模式来,封装好方法,操作的时候直接调方法即可,比较方便。
先看一下.h文件的代码,这样大致就可以知道这个类主要用来干什么:

#import <Foundation/Foundation.h>

@class FMDatabase;
@interface simpleChatCache : NSObject

+(instancetype)sharedChatCache;

//设置用户uid
-(BOOL)initialize:(NSString*)myUserID;
-(void)uninitialize;


//获取所有用户信息
-(void)getUserAll:(void(^)(NSMutableArray*))callback;
//插入或者更新新的数据
-(void)upsert:(NSDictionary*)data;
//设置某个用户未读信息条数为0
-(void)setUnreadNum:(NSString*)targetUserID andNum:(int)num;
//设置某个群未读信息条数为0
-(void)setGroupUnreadNum:(NSString *)targetGroupid andNum:(int)num;
//获取未读消息数
-(void)getUnreadNum:(NSString*)targetUserID andCallback:(void(^)(int))callback;
//获取某个群未读条数
-(void)getGroupUnreadNum:(NSString *)targetGroupid andCallback:(void(^)(int))callback;
//删除某人的聊天记录
-(void)remove:(NSString*)targetUserID;
//删除某一条群聊
-(void)removeGroup:(NSString *)groupid;
//清理数据,除了世界聊天
-(void)clearCachesWithoutWorld;
@end

.m文件:

#import "simpleChatCache.h"
#import "FMDB.h"
FMDatabaseQueue* __g_DB = nil;

@interface simpleChatCache() {
    NSString* _tableName;
    NSString* _userID;
}
@end

@implementation simpleChatCache

+(instancetype)sharedChatCache {
    static simpleChatCache *sharedRunner;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedRunner = [[simpleChatCache alloc] init];
        if(!__g_DB){
            __g_DB = [FMDatabaseQueue databaseQueueWithPath:[MyControl simpleChatDBPath]];
       }
    });
    return sharedRunner;
}

-(BOOL)initialize:(NSString*)myUserID {
    //NSLog(@"sqlite:%@", [FMDatabase isSQLiteThreadSafe] ? @"线程安全" : @"线程不安全");
    
    if(_userID == myUserID)
        return YES;
    
    [self uninitialize];
    
    _userID = myUserID;
    _tableName = [NSString stringWithFormat:@"message_top%@", _userID];
    
    if(__g_DB) {
        [self checkTableStruct];
    }
    
    return (__g_DB != nil);
}

-(void)uninitialize {
    _userID = nil;
    _tableName = nil;
}

-(void)dealloc {
    if(__g_DB) {
        [__g_DB close];
        __g_DB = nil;
    }
    NSLog(@"simpleChatCache dealloc");
}

-(void)createTable {
    WS(ws);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [__g_DB inDatabase:^(FMDatabase *db) {
            if(![db tableExists:_tableName]) {
                NSString * sql = [NSString stringWithFormat:@"create table if not exists %@(lastchatid,lastchattype,laststrchat,lasttime,nickname,unreadnums,isgroup,groupid,groupname,membercount,gface,srcuid)",_tableName];
                if (![db executeUpdate:sql]) {
                    NSLog(@"simpleChatCache 创建表%@失败", _tableName);
                }
            } else {
                [ws upgradeTable:db];
            }
        }];
    });
}

-(void)upgradeTable:(FMDatabase*)db {
    
}

-(void) checkTableStruct {
    [self createTable];
}

//获取所有用户信息
-(void)getUserAll:(void(^)(NSMutableArray*))callback {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [__g_DB inDatabase:^(FMDatabase *db) {
            
            NSMutableArray* array = [NSMutableArray array];
            
            NSString * sql = [NSString stringWithFormat:@"select srcuid,lastchatid,lastchattype,laststrchat,lasttime,nickname,isgroup,groupid,groupname,membercount,gface,unreadnums  from %@ where srcuid != 0 order by lasttime desc",_tableName];
            FMResultSet* rs = [db executeQuery:sql];
            while (rs.next) {
                NSString * srcuid = [rs stringForColumnIndex:0];
                NSString * lastchatid = [rs stringForColumnIndex:1];
                NSString * lastchattype = [rs stringForColumnIndex:2];
                NSString * laststrchat = [rs stringForColumnIndex:3];
                NSString * lasttime = [rs stringForColumnIndex:4];
                NSString * nickname = [rs stringForColumnIndex:5];
                NSString * isgroup = [rs stringForColumnIndex:6];
                NSString * groupid = [rs stringForColumnIndex:7];
                NSString * gropname = [rs stringForColumnIndex:8];
                NSString * membercount = [rs stringForColumnIndex:9];
                NSString * gface = [rs stringForColumnIndex:10];
                NSString * unreadnums = [rs stringForColumnIndex:11];
                                
                NSMutableDictionary * tempDic = [NSMutableDictionary dictionaryWithCapacity:12];
                [tempDic setValue:lastchatid forKey:@"lastchatid"];
                [tempDic setValue:lastchattype forKey:@"lastchattype"];
                [tempDic setValue:laststrchat forKey:@"laststrchat"];
                [tempDic setValue:lasttime forKey:@"lasttime"];
                [tempDic setValue:nickname forKey:@"nickname"];
                [tempDic setValue:unreadnums forKey:@"nums"];
                [tempDic setValue:srcuid forKey:@"srcuid"];
                [tempDic setValue:isgroup forKey:@"isgroup"];
                [tempDic setValue:groupid forKey:@"groupid"];
                [tempDic setValue:gropname forKey:@"groupname"];
                [tempDic setValue:membercount forKey:@"membercount"];
                [tempDic setValue:gface forKey:@"gface"];
                [array addObject:tempDic];
            }
            [rs close];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                if(callback) {
                    callback(array);
                }
            });
        }];
    });
}


//插入或者更新新的数据
-(void)upsert:(NSDictionary*)data {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [__g_DB inDatabase:^(FMDatabase *db) {
            
            if ([data[@"isgroup"] intValue]) {
                //群聊
                NSString * sql;
                NSString * gropSql = [NSString stringWithFormat:@"select groupid from %@ where groupid = %@",_tableName, data[@"groupid"]];
                int groupid = [db intForQuery:gropSql];
                if (data.count == 3) {
                    //更新groupname
                    if (groupid > 0) {
                        sql = [NSString stringWithFormat:@"update %@ set groupname = '%@' where groupid = %@",
                               _tableName,
                               data[@"groupname"],
                               data[@"groupid"]];
                        [db executeUpdate:sql];
                    }
                    
                }else{
                    if (groupid > 0) {
                        sql = [NSString stringWithFormat:@"update %@ set groupname = '%@',lastchatid = %@,lastchattype = %@,laststrchat = '%@',lasttime = %@,nickname = '%@',membercount = %@,unreadnums = unreadnums + %@ where groupid = %@",
                               _tableName,
                               data[@"groupname"],
                               data[@"lastchatid"],
                               data[@"lastchattype"],
                               data[@"laststrchat"],
                               data[@"lasttime"],
                               data[@"nickname"],
                               data[@"membercount"],
                               data[@"nums"],
                               data[@"groupid"]];
                    }else{
                        sql = [NSString stringWithFormat:@"insert into %@(lastchatid,lastchattype,laststrchat,lasttime,nickname,unreadnums,srcuid,isgroup,groupid,groupname,membercount) values (%@,%@,'%@',%@,'%@',%@,%@,%@,%@,'%@',%@)",
                               _tableName,
                               data[@"lastchatid"],
                               data[@"lastchattype"],
                               data[@"laststrchat"],
                               data[@"lasttime"],
                               data[@"nickname"],
                               data[@"nums"],
                               data[@"srcuid"],
                               data[@"isgroup"],
                               data[@"groupid"],
                               data[@"groupname"],
                               data[@"membercount"]];
                    }
                    
                    [db executeUpdate:sql];
                }
                
            }else{
                NSString * sql = [NSString stringWithFormat:@"select srcuid from %@ where srcuid = %@",_tableName, data[@"srcuid"]];
                
                int srcuid = [db intForQuery:sql];
                
                if(srcuid > 0) {
                    if([[data allKeys] containsObject:@"nickname"]) {
                        //更新私聊
                        sql = [NSString stringWithFormat:@"update %@ set lastchatid = %@,lastchattype = %@,laststrchat = '%@',lasttime = %@,nickname = '%@',unreadnums = unreadnums + %@ where srcuid = %@",
                               _tableName,
                               data[@"lastchatid"],
                               data[@"lastchattype"],
                               data[@"laststrchat"],
                               data[@"lasttime"],
                               data[@"nickname"],
                               data[@"nums"],
                               data[@"srcuid"]];
                    } else {
                        //系统消息
                        sql = [NSString stringWithFormat:@"update %@ set lastchatid = %@,lastchattype = %@,laststrchat = '%@',lasttime = %@,unreadnums = unreadnums + %@ where srcuid = %@",
                               _tableName,
                               data[@"lastchatid"],
                               data[@"lastchattype"],
                               data[@"laststrchat"],
                               data[@"lasttime"],
                               data[@"nums"],
                               data[@"srcuid"]];
                    }
                    [db executeUpdate:sql];
                } else {
                    //插入私聊
                    sql = [NSString stringWithFormat:@"insert into %@(lastchatid,lastchattype,laststrchat,lasttime,nickname,unreadnums,srcuid,isgroup,groupid) values (%@,%@,'%@',%@,'%@',%@,%@,%@,%@)",
                           _tableName,
                           data[@"lastchatid"],
                           data[@"lastchattype"],
                           data[@"laststrchat"],
                           data[@"lasttime"],
                           data[@"nickname"],
                           data[@"nums"],
                           data[@"srcuid"],
                           @"0",
                           @"0"];
                    [db executeUpdate:sql];
                }
            } 
        }];
    });
}


//设置某个用户未读信息条数为0
-(void)setUnreadNum:(NSString*)targetUserID andNum:(int)num {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [__g_DB inDatabase:^(FMDatabase *db) {
            NSString * sql = [NSString stringWithFormat:@"update %@ set unreadnums = %d where srcuid = %@", _tableName,num, targetUserID];
            [db executeUpdate:sql];
        }];
    });
}

//设置某个群未读信息条数为0
-(void)setGroupUnreadNum:(NSString *)targetGroupid andNum:(int)num
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [__g_DB inDatabase:^(FMDatabase *db) {
            NSString * sql = [NSString stringWithFormat:@"update %@ set unreadnums = %d where groupid = %@", _tableName,num, targetGroupid];
            [db executeUpdate:sql];
        }];
    });
}

//获取未读消息数
-(void)getUnreadNum:(NSString*)targetUserID andCallback:(void(^)(int))callback {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [__g_DB inDatabase:^(FMDatabase *db) {
            NSString * sql = [NSString stringWithFormat:@"select unreadnums from %@ where srcuid = %@", _tableName,targetUserID];
            int nums = [db intForQuery:sql];
            dispatch_async(dispatch_get_main_queue(), ^{
                if(callback) {
                    callback(nums);
                }
            });
        }];
    });
}

//获取某个群未读条数
-(void)getGroupUnreadNum:(NSString *)targetGroupid andCallback:(void(^)(int))callback
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [__g_DB inDatabase:^(FMDatabase *db) {
            NSString * sql = [NSString stringWithFormat:@"select unreadnums from %@ where groupid = %@", _tableName,targetGroupid];
            int nums = [db intForQuery:sql];
            dispatch_async(dispatch_get_main_queue(), ^{
                if(callback) {
                    callback(nums);
                }
            });
        }];
    });
}

-(void)remove:(NSString*)targetUserID {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [__g_DB inDatabase:^(FMDatabase *db) {
            NSString * sql = [NSString stringWithFormat:@"delete from %@ where srcuid = %@", _tableName,targetUserID];
            [db executeUpdate:sql];
        }];
    });
}

//删除某一条群聊
-(void)removeGroup:(NSString *)groupid
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [__g_DB inDatabase:^(FMDatabase *db) {
            NSString * sql = [NSString stringWithFormat:@"delete from %@ where groupid = %@", _tableName,groupid];
            [db executeUpdate:sql];
        }];
    });
}

//清理数据
-(void)clearCachesWithoutWorld {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [__g_DB inDatabase:^(FMDatabase *db) {
            NSString * sql = [NSString stringWithFormat:@"delete from %@ where srcuid <> 0", _tableName];
            [db executeUpdate:sql];
        }];
    });
}

@end

我们会看到单例实例化的时候有这样一句代码:

if(!__g_DB){
            __g_DB = [FMDatabaseQueue databaseQueueWithPath:[MyControl simpleChatDBPath]];
}

这句代码可以保证整个程序中只存在一个FMDatabaseQueue的实例对象。
因为首先FMDatabase是不具备线程安全的,如果两个线程中同时操作数据库,就会”is currently in use” ;FMDatabaseQueue其实是一个串行的调度队列(G-C-D),数据库的操作必须是顺序执行,不能两个数据库的操作同时执行,如果是两个线程各自创建了FMDatabaseQueue的实例,线程同时执行时,就会出现相同的数据库操作同时触发,导致”database is locked“,所以如果是一个FMDatabaseQueue实例下,多个线程下同时操作,其实是在排在同一个队列中逐一操作的,没有同时操作。这样就可以保证FMDatabase的数据是线程安全的。
所以在使用FMDB的时候一定要使用FMDatabaseQueue,这个是FMDB用来管理数据库的,可以避免很多麻烦。
另外需要注意的是在使用sql语句的时候,如果涉及到字符串,占位符就需要使用单引号,否则也会遇到问题。

猜你喜欢

转载自blog.csdn.net/aaaaazq/article/details/83961538