iOS中FMDB的基础操作,及日常使用心得

本文介绍了在iOS项目中使用数据库时的优化方法,包括使用系统原生sqlite进行操作,以及如何利用FMDB提高执行速度和内存优化。特别强调了在进行大量数据插入修改操作时,通过事务机制和FMDB的强大力量来提升效率,并解决数据不稳定情况下可能出现的SQL执行错误问题。同时,还指出了FMDB更新后对空值处理的变化,以及如何应对这一变化以保持代码兼容性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于FMDB的介绍我想就不用了吧,毕竟大家能用到也就说明大家有一定的了解了。


FMDB github 下载地址:  https://github.com/ccgus/fmdb


本篇博客主要介绍一下当我们项目中用到数据库操作时候,且进行大量的数据插入修改操作时候,对执行速度和内存的优化。


1.使用iOS系统原生sqlite进行操作

/*

 如果我们需要往表 A里面插入大量数据,则我们会把其对应的字段封装到字典里面

 其中一个字典的键值对为:

 {

    "name" = "小白"

    "age" = 18

    "job" = "死神"

 }

*/

-(void)save:(NSString *)tableName dataArray:(NSMutableArray *)dataArray

{

    NSMutableString *strSql = [NSMutableStringstring];

    

   //我们在sql语句前拼接上该语句,即为其加上了事务机制,大大提高了sql语句的执行效率

    [strSql appendString:@"BEGIN TRANSACTION;"];

   for (int i=0; i < [dataArraycount]; i++)

    {

       NSDictionary *dic = dataArray[i];

        

        NSMutableString *strName = [NSMutableStringstring];

        NSMutableString *strValue = [NSMutableStringstring];

        

       if (dic ==nil || [diccount] ==0) {

           continue;

        }

        

       for (NSString *keyin [dicallKeys])

        {

           //循环字典中键值对,对字段及对应的值进行拼接

            [strNameappendFormat:@"%@,",key];

           if ([[dicobjectForKey:key]isKindOfClass:[NSStringclass]]) {

                [strValueappendFormat:@"'%@',",[dicobjectForKey:key]];

            }else {

                [strValueappendFormat:@"%@,",[dicobjectForKey:key]];

            }

        }

        

       NSString *strName2 = [strNamesubstringToIndex:(strName.length-1)];

       NSString *strValue2 = [strValuesubstringToIndex:(strValue.length-1)];

        

        [strSqlappendFormat:@"INSERT OR REPLACE INTO %@ (%@) VALUES (%@);", tableName, strName2, strValue2];

    }

    [strSql appendString:@"COMMIT TRANSACTION;"];

    

    [self ExecuteSql:strSql];

}


- (BOOL)ExecuteSql:(NSString *)sql

{

    NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);

    NSString *dbPath = [[pathsobjectAtIndex:0]stringByAppendingPathComponent:@"test.db"];

   sqlite3 *sqlitedatabase;

   char *errorMsg;

    

   if (sqlite3_open([dbPathUTF8String], &sqlitedatabase) !=SQLITE_OK)

    {

       sqlite3_close(sqlitedatabase);

       returnNO;

    }

    

   if (sqlite3_exec (sqlitedatabase, [sql UTF8String],NULL,NULL, &errorMsg) !=SQLITE_OK) {

       sqlite3_close(sqlitedatabase);

       NSLog(@"errorMsg = %@", [NSStringstringWithUTF8String:errorMsg]);

       returnNO;

    }

    

    sqlite3_close(sqlitedatabase);

    returnYES;

}


//使用系统sqlite执行sql操作。 (实验证明,这个速度是灰常快的,如果不加事务机制,将会很慢。 但该操作并没有使sql语句参数化,其存在一定的漏洞)

举个例子,如果我们的数据中有一个字段如"name" 其对应的值为 "小白'" (注意小白后面的单引号)

这时候我们的SQL语句可能就会是insert into STU (name, age, job) values ('小白'', '18', '死神')  这样就会因为数据的极大随意性导致SQL语句的执行失败。


2.这时候我们便可以使用FMDB的强大功能,

使用该三方库时候,我们需要导入系统框架libsqlite3.dylib框架。


至于简单的插入操作,在此不做太多介绍,  在此主要介绍一次多次 开启事务多次插入数据的方法


(1) 需要导入的头文件:

#import"FMDBOperator.h"

#import "FMDatabaseQueue.h"


初始化 FMDatabaseQueue 对象

_queue = [FMDatabaseQueuedatabaseQueueWithPath:dbPath];


-(void)save:(NSString *)tableName dataArray:(NSMutableArray *)dataArray

{

   

[_queueinTransaction:^(FMDatabase *db,BOOL *rollback) {

       if (![dbopen]) {

           return;

        }

       for (int i=0; i<[arrayLines count]; i++)

        {

           NSMutableString *strSql = [NSMutableStringstring];

           NSDictionary *dic = arrayLines[i];


           NSMutableString *strName = [NSMutableStringstring];

           NSMutableString *strValue = [NSMutableStringstring];

           NSMutableArray *valuesArray = [NSMutableArrayarray];

                

           if (dic ==nil || [diccount] ==0) {

             continue;

            }

                

           for (NSString *keyin [dicallKeys])

           {

               [strNameappendFormat:@"%@,",key];

               [strValueappendString:@"?,"];

               [valuesArrayaddObject:dic[key]];

           }

                

           NSString *strName2 = [strNamesubstringToIndex:(strName.length-1)];

   NSString *strValue2 = [strValuesubstringToIndex:(strValue.length-1)];

                

           [strSqlappendFormat:@"insert or replace into %@ (%@) values (%@)", tableName, strName2, strValue2];

                

           BOOL result = [dbexecuteUpdate:strSqlwithArgumentsInArray:valuesArray];

           if (!result) {

                NSLog(@"数据下载失败:%@, valueArray:%@", tableName, valuesArray);

           }

        }

    }];

}

//使用第二种方法则有效地避免了第一种 在数据不稳定的情况下出现的SQL数据执行错误


下面在随便说一下自己再用到FMDB时候遇到的坑

由于之前一直用的老版本的FMDB三方库,在进行数据检索时候 检索出来的空数据(即该字段的value为空)都被FMDB给过滤掉了,代码如下

NSMutableArray *array = [[NSMutableArrayalloc]init];

       FMResultSet *resultSet = [databaseexecuteQuery:sql];

        

       while ([resultSetnext])

        {

           NSDictionary *resultDict = [resultSetresultDictionary];

            [arrayaddObject:resultDict];

        }

        

        [databaseclose];

举个例子,如果我们数据库中 存在一条数据 (表字段为 id, name, age, job) 其job字段对应的value为空,那么我们执行上面的sql语句检索出来的数据字典格式为

{

"id" = 1,

"name" = "xiaoming",

"age" = 22,

}

其job字段的值FMDB会自动为我们过滤掉


而最新的FMDB中检索出来的数据会是

{

"id" = 1,

"name" = "xiaoming",

"age" = 22,

"job" = "<null>"

}

其会自动把值为空的字段为我们添加一个NSNull的对象进去。 这样的话之前项目里面如果存在 判断 


if (dict[@"job"] != nil) 才执行的语句现在就可能会无效,因为它永远不能为nil,而是一个属于NSNull的对象。


对于我这种不想动之前项目的懒人来说,只能考虑稍微动下库里的东西了


(1)打开 FMResultSet.m 文件,找到 

- (id)objectForColumnIndex:(int)columnIdx 方法,做如下修改




把红框内的内容加以注释,

(2)在同一文件中找到

- (NSDictionary*)resultDictionary 方法内把for循环替换为


for (columnIdx =0; columnIdx < columnCount; columnIdx++) {

      NSString *columnName = [NSStringstringWithUTF8String:sqlite3_column_name([_statementstatement], columnIdx)];

     id objectValue = [selfobjectForColumnIndex:columnIdx];

      //在该处加判断,保证不添加空值nil

     if (objectValue) {

         [dictsetObject:objectValueforKey:columnName];

      }

}

修改之后,在检索数据时候,为空的字段便不会被添加上NSNull对象了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值