美文网首页程序员iOS DeveloperiOS开发
iOS-BUG-The FMDatabase is curren

iOS-BUG-The FMDatabase is curren

作者: 费宇超 | 来源:发表于2017-03-27 15:52 被阅读739次

    BUG现象

    报错 The FMDatabase is currently in use
    报错 Closing leaked statement

    写在前面

    之前写DEMO的时候其实存储数据用到的NSKeyedArchiver序列化和反序列化比较多。代码简单,存储到沙盒也比较安全。只是存储的模型数据需要实现NScoding协议。

     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                        [NSKeyedArchiver archiveRootObject:_dataSource toFile:MLBCacheHomeItemFilePath];
                    });
    

    沙盒的安全性包装第三方软件不能拿到你的app沙盒中的数据,这也是为什么不要金山软件等清理垃圾数据的原因,它只是做了一个假动画,创造了相同数量的垃圾,然后再清除自己。

    但之前有一个即时聊天的APP,会有大量的存数据,查消息的操作,性能需要数据库实现。

    一开始我看着网上的一些例子基本是这么写的

    #pragma mark - 接口
    
    - (void)addPerson:(Person *)person{
        [_db open];
    
        NSNumber *maxID = @(0);
    
        FMResultSet *res = [_db executeQuery:@"SELECT * FROM person "];
        //获取数据库中最大的ID
        while ([res next]) {
            if ([maxID integerValue] < [[res stringForColumn:@"person_id"] integerValue]) {
                maxID = @([[res stringForColumn:@"person_id"] integerValue] ) ;
            }
        }
        maxID = @([maxID integerValue] + 1);
    
        [_db executeUpdate:@"INSERT INTO person(person_id,person_name,person_age,person_number)VALUES(?,?,?,?)",maxID,person.name,@(person.age),@(person.number)];
        [_db close];
    }
    

    确实在一开始的使用情况下,没有出现什么问题。
    可是在我们的APP开始大量数据暴力测试的时候,高量的多线程快速写入查询下。出现了闪退,报错信息如下:

    2017-03-01 15:12:51.987912 traffic.fm[4186:1323093] DB Query: INSERT INTO message(id,type,geo_location,geo_path,radius,expire_date,title,description,content,head_img_url,link_url,priority,pub_user_id,geo_hash,gmt_create,gmt_modified)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
    2017-03-01 15:12:51.987987 traffic.fm[4186:1323093] Unknown error finalizing or resetting statement (10: disk I/O error)
    2017-03-01 15:12:51.988047 traffic.fm[4186:1323093] DB Query: INSERT INTO message(id,type,geo_location,geo_path,radius,expire_date,title,description,content,head_img_url,link_url,priority,pub_user_id,geo_hash,gmt_create,gmt_modified)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
    
    
    
    2017-03-01 16:26:42.012233 traffic.fm[2606:1262649] The FMDatabase <FMDatabase: 0x170082f30> is currently in use.
    2017-03-01 16:26:42.012397 traffic.fm[2606:1262649] The FMDatabase <FMDatabase: 0x170082f30> is currently in use.
    2017-03-01 16:26:42.012478 traffic.fm[2606:1262649] Closing leaked statement
    

    原因是在多线程下,高并发下不同线程对数据的竞争,对一个数据进行重复写入修改。

    需要使用队列实现才能避免这样的问题

    正确的姿势 请躺好

    //  Created by frank on 16/11/23.
    //  Copyright © 2016年 TopHeavier. All rights reserved.
    //
    
    #import "MessageDatabase.h"
    #import <FMDB.h>
    #import "FMDatabaseQueue.h"
    
    
    static MessageDatabase *_instance = nil;
    
    @interface MessageDatabase()
    @property (nonatomic, strong) FMDatabaseQueue *queue;
    @end
    
    @implementation MessageDatabase
    
    +(instancetype)sharedDataBase{
        
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [[self alloc] init];
        });
        
        return _instance;
    }
    
    
    // 懒加载数据库队列
    - (FMDatabaseQueue *)queue {
        if (_queue == nil) {
            NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
            // 文件路径
            NSString *filePath = [documentsPath stringByAppendingPathComponent:@"model.sqlite"];
            _queue = [FMDatabaseQueue databaseQueueWithPath:filePath];
        }
        return _queue;
    }
    
    -(void)initDataBase{
        // 获得Documents目录路径
        NSString * sql = @"CREATE TABLE IF NOT EXISTS 'message'  ('id' VARCHAR(64) PRIMARY KEY ,'type' int(1),'geo_location' VARCHAR(64),'geo_path' VARCHAR(300),'radius'int(11),'expire_date' timestamp,'title' VARCHAR(64),'description' VARCHAR(200),'content' VARCHAR(1200),'head_img_url' VARCHAR(300),'link_url' VARCHAR(64),'priority' int(11),'pub_user_id' varchar(64),'geo_hash' VARCHAR(64),'gmt_create' timestamp,'gmt_modified' timestamp) ";
        [self.queue inDatabase:^(FMDatabase *db) {
            BOOL result = [db executeUpdate:sql withArgumentsInArray:nil];
            if (result) {
                NSLog(@"创建表格成功");
            } else {
                NSLog(@"创建表格失败");
            }
        }];
        
      
    }
    
    #pragma mark - 接口
    
    -(void)addMessage:(MessageObject *)MessageObject{
        [self.queue inDatabase:^(FMDatabase *db) {
            BOOL result =   [db executeUpdate:@"INSERT OR IGNORE INTO message(id,type,geo_location,geo_path,radius,expire_date,title,description,content,head_img_url,link_url,priority,pub_user_id,geo_hash,gmt_create,gmt_modified)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",MessageObject.uniqueId,MessageObject.type,MessageObject.geoLocation,MessageObject.geoPath,MessageObject.radius,MessageObject.expireDate,MessageObject.title,MessageObject.messageDescription,MessageObject.content,MessageObject.headImgUrl,MessageObject.linkUrl,MessageObject.priority,MessageObject.pubUserId,MessageObject.geoHash,MessageObject.gmtCreate,MessageObject.gmtModified];;
            if (result) {
                NSLog(@"插入数据成功");
            } else {
                NSLog(@"插入数据失败");
            }
        }];
        
        
    }
    
    
    
    -(NSMutableArray *)searchRecentMessage:(NSNumber *)num{
      
        NSMutableArray *dataArray = [[NSMutableArray alloc] init];
            [self.queue inDatabase:^(FMDatabase *db) {
            FMResultSet * res =   [db executeQuery:@"SELECT * FROM message order by gmt_create desc limit ? ",num];;
            if (res) {
                while ([res next]) {
                }
            } else {
                NSLog(@"查询最近数据失败");
            }
        }];
        
        NSEnumerator *enumerator = [dataArray reverseObjectEnumerator];
        NSMutableArray *reseverArr= [[NSMutableArray alloc]initWithArray: [enumerator allObjects]];
        return reseverArr;
        
    }
    
    -(NSMutableArray *)getMessageBefore:(NSDate *)startTime pageSize:(NSNumber *)pageSize page:(NSNumber *)page {
        NSMutableArray *dataArray = [[NSMutableArray alloc] init];
        NSInteger offsetInterValue  = pageSize.intValue-1;
        NSNumber * offset = [NSNumber numberWithInteger:offsetInterValue];
    
        [self.queue inDatabase:^(FMDatabase *db) {
            FMResultSet *res = [db executeQuery:@"SELECT * FROM message where gmt_create <=? order by gmt_create asc limit ? offset ?",startTime,pageSize,offset];
            if (res) {
                while ([res next]) {
                }
            } else {
                NSLog(@"查询之前消息失败");
            }
        }];
    
            return dataArray;
    }
    
    
    -(void)deleteMessage:(MessageObject *)MessageObject{
        [self.queue inDatabase:^(FMDatabase *db) {
            BOOL result =   [db executeUpdate:@"DELETE FROM message WHERE id = ?",MessageObject.uniqueId];
            if (result) {
                NSLog(@"删除数据成功");
            } else {
                NSLog(@"删除数据失败");
            }
        }];
    }
    
    - (NSMutableArray *)getAllMessage{
    
        NSMutableArray *dataArray = [[NSMutableArray alloc] init];
        
        [self.queue inDatabase:^(FMDatabase *db) {
            FMResultSet *res = [db executeQuery:@"SELECT * FROM message"];
            while ([res next]) {
        }
        }];
        return dataArray;
    
    }
    
    
    @end
    
    

    这个例子告诉我们有时候可能你觉得自己很水,但网上比你水的人更多。

    用新东西的时候一定要了解其底层实现原理,要参考就参考官方库的例子。

    做技术要自信。

    官方地址

    相关文章

      网友评论

        本文标题:iOS-BUG-The FMDatabase is curren

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