草庐IT

ios - FMDB 阻塞用户界面。但为什么?对替代实现有什么建议吗?

coder 2024-01-16 原文

我有一个使用 FMDB 并在应用程序启动后立即执行更新(仅一次)的应用程序。更新非常繁重,需要 12-20 秒来处理。我将 FMDatabaseQueue 与基于单例类的事务一起使用。

========DB.h ====================

@interface DB : NSObject{
    FMDatabaseQueue *dbqueue;
    NSArray *queriesCreateSchema;
    NSString *dbFullPath;
}

@property (nonatomic, strong) FMDatabaseQueue *dbqueue;

==================================

========DB.m ====================

- (id)initWithFullPath: (NSString*)fullPath {
    if (self = [super init]) {

        dbFullPath = fullPath;

        //Opening/Creating the serial queue
        dbqueue = [FMDatabaseQueue databaseQueueWithPath:dbFullPath];
        if (dbqueue == nil){
            return nil;
        }

        queriesCreateSchema = [NSArray arrayWithObjects:DBQUERY_ENABLE_FOREIGN_KEYS,
                               DBQUERY_CREATE_DB,
                               DBQUERY_CREATE_USERS,
                               DBQUERY_CREATE_BOOKS,
                               DBQUERY_INDEX_BOOKS,
                               DBQUERY_CREATE_BOOKUSER,
                               DBQUERY_CREATE_PAGES,
                               DBQUERY_CREATE_PAGEUSER,
                               DBQUERY_CREATE_TAGS,
                               DBQUERY_CREATE_TAGBOOK,
                               DBQUERY_CREATE_CATEGORIES,
                               DBQUERY_CREATE_CATBOOK,
                               nil];
    }
    return self;
}

================================== ======== DBManager.h ====================

@interface DBManager : NSObject <CommsManagerDelegate> {
    __weak id <DBMDelegate>  delegate;
    DB *database;
    NSString *dbFullPath;
}


@property (nonatomic, weak) id <DBMDelegate> delegate;
@property (nonatomic, strong) DB *database;
@property (nonatomic, strong) NSString *dbFullPath;

=========================================

======== DBManager.m ====================

-(BOOL) parseMetaDataDict: (NSDictionary*) serverDict {

    /// Getting the lists of books from the Server's JSON dictionary
    NSDictionary *dictAllBooks = [serverDict objectForKey:@"Books"];

    int bookNum=0;
    int totalBooks = [[dictAllBooks valueForKey:@"Book"] count];

    // Updates the UI
    [delegate dbmNumberOfBooksProcessedByDB:totalBooks];

    /// Browsing it book by book
    for (id serverDictBook in [dictAllBooks valueForKey:@"Book"]){
        bookNum++;

        /// Trimming book from the server and placing it into the local book dictionary
        BookDict *bookDict = [[BookDict alloc]initWithServerDict:serverDictBook];

        __block BOOL isError = NO;

        /// Sending the queries into the serial queue
        [database.dbqueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
            /// Inserting book into the BOOKS table
            if(![db executeUpdate:DBUPDATE_INSERT_BOOK withParameterDictionary:bookDict.dictionary])
            {
                isError = YES;
                DDLogWarn(@"%@", [db lastErrorMessage]);
                *rollback = YES;
                return;      // Carefull - It returns from the transaction, not the function
            }
        }];

        if (isError){
            return NO;
        }

        __block NSString *bookID;

        /// Getting the bookID automatically generated by the DB
        NSString *query = [NSString stringWithFormat:@"SELECT bookID FROM BOOKS where isbn = '%@'", [bookDict.dictionary valueForKey:@"isbn"]];
        [database.dbqueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
            FMResultSet *result = [db executeQuery:query];
            if([result next])
            {
                int num = [result intForColumnIndex:0];
                bookID = [NSString stringWithFormat:@"%d", num];
            }
            else{
                isError = YES;
                DDLogWarn(@"%@", [db lastErrorMessage]);
                *rollback = YES;
                return;      // Carefull - It returns from the transaction, not the function
            }
        }];

        if (isError){
            return NO;
        }


        int numPages = [[serverDictBook objectForKey:@"numberOfPages"] intValue];

        /// Browsing the book page by page
        ///VCC Today probably replace by 0
        for (int i=1; i<=numPages; i++)
        {
            PageDict *pageDict = [[PageDict alloc]initWithPage:i andBookID:bookID ofServerDict:serverDictBook];

            __block BOOL isError = NO;

            /// Sending the queries into the serial queue
            [database.dbqueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
                /// Inserting page into the PAGES table
                if(![db executeUpdate:DBUPDATE_INSERT_PAGE withParameterDictionary:pageDict.dictionary])
                {
                    isError = YES;
                    DDLogWarn(@"%@", [db lastErrorMessage]);
                    *rollback = YES;
                    return;      // Carefull - It returns from the transaction, not the function
                }
            }];

            if (isError)
            return NO;
        }


        __block NSString *catID;

        /// Browsing the book categories one by one
        for (id serverCatDict in [serverDictBook valueForKey:@"categories"]){

            __block BOOL isError = NO;

            /// Sending the queries into the serial queue
            [database.dbqueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
                /// Inserting row into the CATEGORY table
                if(![db executeUpdate:DBUPDATE_INSERT_CATEGORY withParameterDictionary:serverCatDict])
                {
                    isError = YES;
                    DDLogWarn(@"%@", [db lastErrorMessage]);
                    *rollback = YES;
                    return;      // Carefull - It returns from the transaction, not the function
                }

                /// Getting the catID automatically generated by the DB
                NSString *query = [NSString stringWithFormat:@"SELECT catID FROM CATEGORIES where name = '%@'", [serverCatDict valueForKey:@"name"]];

                FMResultSet *result = [db executeQuery:query];
                if([result next])
                {
                  catID = [result stringForColumnIndex:0];
                }
                else{
                  isError = YES;
                  DDLogError(@"%@", [db lastErrorMessage]);
                  *rollback = YES;
                  return;      // Carefull - It returns from the transaction, not the function
                }

              CatBookDict *catBookDict = [[CatBookDict alloc] initWithCatID:catID andBookID:bookID];

              /// Inserting row into the CATBOOK table
              if(![db executeUpdate:DBUPDATE_INSERT_CATBOOK withParameterDictionary:catBookDict.dictionary])
              {
                isError = YES;
                DDLogError(@"%@", [db lastErrorMessage]);
                *rollback = YES;
                return;      // Carefull - It returns from the transaction, not the function
              }

          }];

          if (isError)
              return NO;

        }


//      /// Browsing the book categories one by one
//      for (id serverCatDict in [serverDictBook valueForKey:@"name"]){
//        
//          __block BOOL isError = NO;
//
//        CatBookDict *catBookDict = [[CatBookDict alloc] initWithCatID:[serverCatDict valueForKey:@"catID"]];
//
//          /// Sending the queries into the serial queue
//          [database.dbqueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
//                                  andBookID:bookID];
//              /// Inserting row into the CATBOOK table
//              if(![db executeUpdate:DBUPDATE_INSERT_CATBOOK withParameterDictionary:catBookDict.dictionary])
//              {
//                isError = YES;
//                DDLogVerbose(@"%@", [db lastErrorMessage]);
//                *rollback = YES;
//                return;      // Carefull - It returns from the transaction, not the function
//              }
//          }];
//                                      
//          if (isError)
//          return NO;
//
//      }
      [database.dbqueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        FMResultSet *result = [db executeQuery:query];
        if([result next])
        {
          int num = [result intForColumnIndex:0];
          bookID = [NSString stringWithFormat:@"%d", num];
        }
        else{
          isError = YES;
          DDLogError(@"%@", [db lastErrorMessage]);
          *rollback = YES;
          return;      // Carefull - It returns from the transaction, not the function
        }
      }];

      if (isError){
        return NO;
      }



        /// Browsing the book tags one by one
        for (id serverTagDict in [serverDictBook valueForKey:@"tags"]){
//            TagDict *tagDict = [[TagDict alloc] initWithServerDict:serverTagDict[0]];
//            TagBookDict *tagBookDict = [[TagBookDict alloc] initWithTagID:[serverTagDict valueForKey:@"tagID"]
//                                                                andBookID:bookID];
            __block BOOL isError = NO;

            /// Sending the queries into the serial queue
            [database.dbqueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
                /// Inserting tag into the TAGS table
                if(![db executeUpdate:DBUPDATE_INSERT_TAG withParameterDictionary:serverTagDict])
                {
                    isError = YES;
                    DDLogError(@"%@", [db lastErrorMessage]);
                    *rollback = YES;
                    return;      // Carefull - It returns from the transaction, not the function
                }

//                /// Inserting the row into the TAGBOOK table
//                if(![db executeUpdate:DBUPDATE_INSERT_TAGBOOK withParameterDictionary:tagBookDict.dictionary])
//                {
//                    isError = YES;
//                    DDLogVerbose(@"%@", [db lastErrorMessage]);
//                    *rollback = YES;
//                    return;      // Carefull - It returns from the transaction, not the function
//                }

            }];

            if (isError)
                return NO;
        }

        // Updates the UI
        [delegate dbmBookProcessedByDB:bookNum];

    }

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    if (![defaults objectForKey:@"firstSynced"]){
        [defaults setObject:[NSDate date] forKey:@"firstSynced"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }

    return TRUE;

我有一个 View Controller ,它通过调用前面的方法“parseMetaDataDict”来使用 DBManager 类。对于 for a book 的每次迭代,都会处理并在数据库中进行插入。我正在使用委托(delegate)并在循环中更新 UI。

=============SplashViewController.h ============================

-(void)dbmBookProcessedByDB:(int)bookNum{

    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
        NSString *strMsg = [NSString stringWithFormat:@"DB Processing Book %d / %d", bookNum, _totalBooksToBeDownloaded];
        [self.progressBar setText:strMsg];;

        if (bookNum == _totalBooksToBeDownloaded){
                [self.progressBar setText:@"Books library has successfully been updated"];
                [self.progressBar setNeedsDisplay];
                [self performSegueWithIdentifier:@"splashToHome" sender:self];
        }

    });

}

=========================================

进度条的更新没有发生。我相信 dispatch_async 是不必要的。调试显示它通过了 dispatch_async 并且从未进入内部。

是不是FMDB队列阻塞了整个Main线程。每次处理书籍时如何定期更新标签?

最佳答案

在 FMDB 中,dispatch_sync 函数用于将您的事务 block 放入串行队列。 Documentation对于 dispatch_sync 说:

As an optimization, this function invokes the block on the current thread when possible.

我认为这就是为什么调用 -inTransaction: 可能会阻塞主线程。

尝试让 -inTransaction: 从后台线程调用。为此,您可以将 for 循环的主体放入 background queue 中。通过 CGD 像这样:

-(BOOL) parseMetaDataDict: (NSDictionary*) serverDict {
    ...

    /// Browsing it book by book
    for (id serverDictBook in [dictAllBooks valueForKey:@"Book"]){
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void){

            ... all -inTransaction: calls are here

            dispatch_async(dispatch_get_main_queue(), ^(void){
                // Updates the UI
                [delegate dbmBookProcessedByDB:bookNum];
            });

        });
    }

注意:最好在同一个范围内的线程之间跳转,让代码看起来更清晰,所以你也可以从 -dbmBookProcessedByDB 移动 dispatch_async(dispatch_get_main_queue(), ...) inside for body 如上面的代码所示。

关于ios - FMDB 阻塞用户界面。但为什么?对替代实现有什么建议吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22666167/

有关ios - FMDB 阻塞用户界面。但为什么?对替代实现有什么建议吗?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  6. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  7. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  8. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  9. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  10. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

随机推荐