2014-03-26 32 views
2

我有一个应用程序使用FMDB并在应用程序启动后立即执行更新(只有一次)。更新非常繁重,需要12-20秒才能完成。我正在使用基于单例类的事务的FMDatabaseQueue。FMDB阻止用户界面。但为什么?任何建议的替代实施?

======== 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; 

}

我有一个使用一个视图控制器DBManager类通过调用前面的方法“parseMetaDataDict”。对于书籍的每一次迭代都会被处理,插入将在数据库中进行。我正在使用委托并更新循环内的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队列是否阻塞整个主线程。每次处理书籍时,如何定期更新标签?

回答

3

在FMDB中,dispatch_sync函数用于将事务块放入串行队列。 Documentationdispatch_sync说:

作为一种优化,这个函数调用了当前 线程块时可能。

我认为这就是为什么要调用-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]; 
      }); 

     }); 
    } 

注:这也是更好地在一个范围内的线程之间跳转,使代码看起来清晰,所以你也可以如上面的代码所示,在for内移动dispatch_async(dispatch_get_main_queue(), ...)-dbmBookProcessedByDB