我有一个使用 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 移动 inside dispatch_async(dispatch_get_main_queue(), ...) for body 如上面的代码所示。
关于ios - FMDB 阻塞用户界面。但为什么?对替代实现有什么建议吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22666167/
类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
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用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
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
在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',
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到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类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数