与this SO question相关,我想将数据加载到后台。
但是,我得到'库例程称为不按顺序'错误。
在this SO thread中说方式是使用NSOperation,但是查看网络上的示例我不知道如何解决这个问题。
我与单例模式共享一个sqlite连接:
@interface Db : NSObject {
NSString *path;
FMDatabase* theDb;
BOOL isOpen;
}
@property (retain, nonatomic) FMDatabase *theDb;
@property (retain, nonatomic) NSString *path;
@property (nonatomic) BOOL isOpen;
--------
static Db *currentDbSingleton = nil;
#pragma mark Global access
+(id)currentDb {
@synchronized(self) {
if (!currentDbSingleton) {
NSString *reason = NSLocalizedString(@"The database is not set globally",
@"Error Db: database is not set");
NSException *e = [NSException exceptionWithName:@"DBError"
reason:reason;
userInfo:nil];
@throw e;
}
}
return currentDbSingleton;
}
所以更难打开两次相同的数据库......
有什么想法吗?
修改
我确认错误是在调用sqlite时。我使用FDBM作为瘦包装来调用它。
我正在运行2个线程:主要和后台任务,用于加载数据。我这样运行:
- (void) fillCache:(NSString *)theTable {
[NSThread detachNewThreadSelector:@selector(fillCacheBackground:)
toTarget:self
withObject:theTable];
}
- (void)loadComplete {
[self.table reloadData];
}
- (void) fillCacheBackground:(NSString *)theTable {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Db *db= [Db currentDb];
[db beginTransaction];
..... STUFF HERE
[db commitTransaction];
//Tell our callback what we've done
[self performSelectorOnMainThread:@selector(loadComplete)
withObject:nil
waitUntilDone:YES];
[pool drain];
}
db接口的代码位于http://code.google.com/p/chibiorm/source/browse/#svn/trunk/src - 特别是Db.h / m,它是与fdbm / sqlite接口的唯一单元。
尝试从FDBM调用sqlite函数时发生错误。
例如发生在这里:
-(void) checkError {
if ([self.theDb hadError]) { // <====ERROR HERE
NSLog(@"Err %d: %@", [self.theDb lastErrorCode], [self.theDb]);
}
}
这称为FDBM代码:
- (BOOL) hadError {
int lastErrCode = sqlite3_errcode(db);
return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW);
}
答案 0 :(得分:3)
单例方法是一个好主意,虽然看起来你实际上并没有在任何地方初始化currentDbSingleton
...假设你修复了这个并且你返回了一个有效的数据库连接,我认为不是你有问题。
你提到的'库程序不按顺序'错误提示我你正在使用的库(SQLite或FMDB)需要按特定顺序进行方法/函数调用。您可能遇到的是并发问题,其中两个或多个线程正在调用同一个库,并且每个线程都可以使用正确的顺序,如果它们“同时说话”,可以这么说,库可能会收到呼叫超出预期的顺序。你想要的是将一组调用视为一个原子单元,这样它们就不会重叠或混合。
这是NSOperation的用武之地。对它进行子类化允许您将一堆代码视为“单个封装任务” - 您可能希望设计非并发操作。类文档的详细信息说明了如何在NSOperation中实现逻辑。
编辑:(在提问者用附加背景澄清了问题后)
由于问题发生在checkError
,并且该方法是从链接的.m文件中的多个位置调用的,因此您很可能在错误的时间调用hadError
。 (它是在事务关闭后调用的吗?如果在下一个事务开始后调用它会怎样?)
例如,如果在前一次调用仍在访问数据库时调用fillCache
会发生什么?在这方面,您的交易管理方法看起来非常可疑。例如,如果某人调用beginTransaction
而inTransaction
为YES
,则该方法将返回而不执行任何操作(即,它根本不会调用FMDatabase ivar)。您可能想要的是第二个调用者等到第一个调用者完成其事务。 (除非FMDatabase支持并发事务,否则无论如何都要在其上调用beginTransaction
。)
如果您还没有,请阅读this Apple article on Objective-C thread synchronization。接下来,请参阅NSLock
(特别是lockBeforeDate:
)和相关示例代码的文档。在-[Db beginTransaction]
方法中,您可能希望阻止获取锁定。
你也有几个特殊的类方法,比如+ allocWithZone: - 选择使用+ inizialize(如果你可以的话,运行时会自动调用它),这样类可以负责初始化自己无需手动调用。 (我猜你叫+ alloc,然后是-initWithName:,然后把它反馈给+ setCurrentDb。一个方便的方法,比如+ initializeWithPath:处理所有更干净的东西。)
还有许多其他问题,例如+ setCurrentDb:可以换出单例对象而不管事务是否正在进行(并且旧单例未被释放),+ currentDb会引发异常只需创建单例实例等。然而,您面临的最大问题是正确获得并发性。我认为实现锁以保护FMDatabase引用是朝着正确方向迈出的一步,但只是在NSOperation中包装方法X不会为你做。代码中引用theDb
的每个点都不会保证没有其他人这样做会导致崩溃。如果这看起来很难,请不要感到难过,因为它是。
上次随机建议:将您的方法TypeForField:Type:
和ValueForField:Name:Type:
分别更改为typeForFieldName:typeName:
和valueForResultSet:fieldName:typeName:
。力求准确性,可读性和匹配惯例。
答案 1 :(得分:2)