美文网首页
FMDB源码分析——多线程安全和二次封装

FMDB源码分析——多线程安全和二次封装

作者: 无悔zero | 来源:发表于2021-02-07 19:33 被阅读0次

FMDB是平常我们很经常用的数据库框架,它特点是:

1.使用方便;
2.对比苹果自带CoreData框架更轻量级和灵活;
3.提供多线程安全,原来的SQLite并不是线程安全;
4.还可以把多个任务包装成事务,提高效率。

  • (一)多线程安全

FMDB的线程安全主要在于串行,比如下面例子:

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:self.dbPath];
//多任务并发添加,执行顺序是顺序执行的
dispatch_queue_t queuet = dispatch_queue_create("queuet", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queuet, ^{
    for (int i = 0; i < 100; i ++) {
        [queue inDatabase:^(FMDatabase * _Nonnull db) {
            if ([db open]) {
                BOOL result = [self.db executeUpdate:@"create table if not exists t_database_queue2 (userID integer primary key autoincrement,name text not null,number integer not null);"];
                if (result) {
                    NSString * insertSql1 = [NSString stringWithFormat:@"insert into t_database_queue2 (userID,name,number) values (%d,'%d',%d)",i,i,i];
                    BOOL res = [self.db executeUpdate:insertSql1];
                    if (!res) {
                        NSLog(@"失败%d",i);
                    }
                }
            }
        }];
    }
});

dispatch_async(queuet, ^{
    for (int i = 0; i < 100; i ++) {
        [queue inDatabase:^(FMDatabase * _Nonnull db) {
            if ([db open]) {
                BOOL result = [self.db executeUpdate:@"create table if not exists t_database_queue2 (userID integer primary key autoincrement,name text not null,number integer not null);"];
                if (result) {
                    NSString * insertSql1 = [NSString stringWithFormat:@"insert into t_database_queue2 (userID,name,number) values (%d,'%d',%d)",i+50,i,i];
                    BOOL res = [self.db executeUpdate:insertSql1];
                    if (!res) {
                        NSLog(@"失败%d",i);
                    }
                }
            }
        }];
    }
});
  1. 我们从源码可以看到:
- (void)inDatabase:(__attribute__((noescape)) void (^)(FMDatabase *db))block {
    ...
    //同步串行队列中执行
    dispatch_sync(_queue, ^() {
        
        FMDatabase *db = [self database];
        block(db);
        
        if ([db hasOpenResultSets]) {            
#if defined(DEBUG) && DEBUG
            NSSet *openSetCopy = FMDBReturnAutoreleased([[db valueForKey:@"_openResultSets"] copy]);
            for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) {
                FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue];
            }
#endif
        }
    });
    
    FMDBRelease(self);
}
  • (二)二次封装

每次都手写sql语句和处理对应model很麻烦,所以进行二次封装,直接参考别人的代码:

  1. 保存数据
- (BOOL)jq_insertTable:(NSString *)tableName dicOrModel:(id)parameters
{
    NSArray *columnArr = [self getColumnArr:tableName db:_db];//获取表的字段
    return [self insertTable:tableName dicOrModel:parameters columnArr:columnArr];
}

- (BOOL)insertTable:(NSString *)tableName dicOrModel:(id)parameters columnArr:(NSArray *)columnArr
{
    BOOL flag;
    NSDictionary *dic;

    if ([parameters isKindOfClass:[NSDictionary class]]) {
        dic = parameters;
    }else {
        dic = [self getModelPropertyKeyValue:parameters tableName:tableName clomnArr:columnArr];//runtime获取属性字段
    }
    
    //拼接 Sql 语句
    NSMutableString *finalStr = [[NSMutableString alloc] initWithFormat:@"INSERT INTO %@ (", tableName];//sql
    NSMutableString *tempStr = [NSMutableString stringWithCapacity:0];
    NSMutableArray *argumentsArr = [NSMutableArray arrayWithCapacity:0];
    
    for (NSString *key in dic) {
        if (![columnArr containsObject:key] || [key isEqualToString:@"pkid"]) {
            continue;
        }
        [finalStr appendFormat:@"%@,", key];
        [tempStr appendString:@"?,"];
        [argumentsArr addObject:dic[key]];//添加value
    }
    
    [finalStr deleteCharactersInRange:NSMakeRange(finalStr.length-1, 1)];
    if (tempStr.length)
        [tempStr deleteCharactersInRange:NSMakeRange(tempStr.length-1, 1)];
    
    [finalStr appendFormat:@") values (%@)", tempStr];
    
    flag = [_db executeUpdate:finalStr withArgumentsInArray:argumentsArr];
    return flag; //拼接绑定的操作
}
// 获取model的key和value
- (NSDictionary *)getModelPropertyKeyValue:(id)model tableName:(NSString *)tableName clomnArr:(NSArray *)clomnArr
{
    NSMutableDictionary *mDic = [NSMutableDictionary dictionaryWithCapacity:0];
    unsigned int outCount;
    objc_property_t *properties = class_copyPropertyList([model class], &outCount);//runtime获取属性字段
    
    for (int i = 0; i < outCount; i++) {
        NSString *name = [NSString stringWithCString:property_getName(properties[i]) encoding:NSUTF8StringEncoding];
        if (![clomnArr containsObject:name]) {
            continue;
        }
        id value = [model valueForKey:name];
        if (value) {
            [mDic setObject:value forKey:name];
        }
    }
    free(properties);
    
    return mDic;
}
  1. 读取数据
//数据库数据->model
- (NSArray *)jq_lookupTable:(NSString *)tableName dicOrModel:(id)parameters whereFormat:(NSString *)format, ...
{
    va_list args;
    va_start(args, format);
    NSString *where = format?[[NSString alloc] initWithFormat:format locale:[NSLocale currentLocale] arguments:args]:format;
    va_end(args);
    NSMutableArray *resultMArr = [NSMutableArray arrayWithCapacity:0];
    NSDictionary *dic;
    NSMutableString *finalStr = [[NSMutableString alloc] initWithFormat:@"select * from %@ %@", tableName, where?where:@""];
    NSArray *clomnArr = [self getColumnArr:tableName db:_db];
    
    FMResultSet *set = [_db executeQuery:finalStr];
    //字典
    if ([parameters isKindOfClass:[NSDictionary class]]) {
        ...
    }else {
        Class CLS;
        if ([parameters isKindOfClass:[NSString class]]) {
            if (!NSClassFromString(parameters)) {
                CLS = nil;
            } else {
                CLS = NSClassFromString(parameters);
            }
        } else if ([parameters isKindOfClass:[NSObject class]]) {//model类型
            CLS = [parameters class];
        } else {
            CLS = parameters;
        }
        
        if (CLS) {
            NSDictionary *propertyType = [self modelToDictionary:CLS excludePropertyName:nil];//runtime获取属性字段
            
            while ([set next]) {
                //kvc赋值
                id model = CLS.new;
                for (NSString *name in clomnArr) {
                    if ([propertyType[name] isEqualToString:SQL_TEXT]) {
                        id value = [set stringForColumn:name];
                        if (value)
                            [model setValue:value forKey:name];
                    } else if ([propertyType[name] isEqualToString:SQL_INTEGER]) {
                        [model setValue:@([set longLongIntForColumn:name]) forKey:name];
                    } else if ([propertyType[name] isEqualToString:SQL_REAL]) {
                        [model setValue:[NSNumber numberWithDouble:[set doubleForColumn:name]] forKey:name];
                    } else if ([propertyType[name] isEqualToString:SQL_BLOB]) {
                        id value = [set dataForColumn:name];
                        if (value)
                            [model setValue:value forKey:name];
                    }
                }
                [resultMArr addObject:model];
            }
        }
    }
    return resultMArr;
}
- (NSDictionary *)modelToDictionary:(Class)cls excludePropertyName:(NSArray *)nameArr
{
    NSMutableDictionary *mDic = [NSMutableDictionary dictionaryWithCapacity:0];
    unsigned int outCount;
    objc_property_t *properties = class_copyPropertyList(cls, &outCount);//runtime获取属性字段
    for (int i = 0; i < outCount; i++) {
        NSString *name = [NSString stringWithCString:property_getName(properties[i]) encoding:NSUTF8StringEncoding];
        if ([nameArr containsObject:name]) continue;
        
        NSString *type = [NSString stringWithCString:property_getAttributes(properties[i]) encoding:NSUTF8StringEncoding];
        id value = [self propertTypeConvert:type];
        if (value) {
            [mDic setObject:value forKey:name];
        }
    }
    free(properties);
    
    return mDic;
}

相关文章

网友评论

      本文标题:FMDB源码分析——多线程安全和二次封装

      本文链接:https://www.haomeiwen.com/subject/jcwgtltx.html