FMDB阻止UI。但为什么?有关替代实施的建议吗?

时间:2014-03-26 15:43:42

标签: ios multithreading fmdb

我有一个使用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;

}

我有一个视图控制器,通过调用前面的方法&#34; parseMetaDataDict&#34;来使用DBManager类。对于书籍的每次迭代都会被处理,并且插入会在数据库中进行。我正在使用委托并在循环中更新UI。

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

- (无效)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队列是否阻止整个主线程。每次处理图书时,如何定期更新标签?

1 个答案:

答案 0 :(得分:3)

在FMDB中,dispatch_sync函数用于将事务块放入串行队列。 dispatch_sync的{​​{3}}说:

  

作为优化,此函数调用当前的块   尽可能的线程。

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

尝试从后台线程调用-inTransaction:。为此,您可以通过CGD将for周期的正文放入Documentation,如下所示:

-(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];
            });

        });
    }

注意:最好在一个范围内的线程之间跳转,以使代码看起来清晰,这样您也可以从dispatch_async(dispatch_get_main_queue(), ...) -dbmBookProcessedByDB体内for移动{{1}}如上面的代码所示。