sqlite が (wal またはジャーナル ファイルを使用して) トランザクションを実行する方法が原因で、sqlite コードを App Store に移植する際に多くの問題があります。Apple ドキュメントの関連部分は次のとおりです。
アプリは、同じ名前で拡張子が異なる複数の関連ファイルを開いたり保存したりできる必要があります (たとえば、映画ファイルと同じ名前の字幕ファイルを自動的に開いたり、SQLite ジャーナル ファイルを許可したりします)。そのセカンダリ ファイルにアクセスするには、NSFilePresenter プロトコルに準拠するクラスを作成します。このオブジェクトは、メイン ファイルの URL をその primaryPresentedItemURL プロパティとして提供し、セカンダリ ファイルの URL をそのpresentedItemURL プロパティとして提供する必要があります。ユーザーがメイン ファイルを開いた後、ファイル プレゼンター オブジェクトは NSFileCoordinator クラスの addFilePresenter: クラス メソッドを呼び出して自身を登録する必要があります。
Apple DTS から次のコードが提供されます。
- (void)openSQLiteFileAtURL:(NSURL *)fileURL {
NSFileCoordinator *fc = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
[fc coordinateReadingItemAtURL:fileURL options:0 error:NULL byAccessor:^(NSURL *newURL) {
sqlite3 *db = NULL;
char *zErrMsg = 0;
[SQLiteRelatedItemPresenter addPresentersForURL:newURL];
int rc = sqlite3_open_v2([[newURL path] fileSystemRepresentation], &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
NSLog(@"open %@ = %d", [newURL path], rc);
rc = sqlite3_exec(db, "CREATE TABLE foo (col1 INTEGER);", callback, 0, &zErrMsg);
if( rc!=SQLITE_OK ){
NSLog(@"SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
// more sqlite code here
sqlite3_close(db);
}];
return;
}
ここで、SQLiteRelatedItemPresenter は次のとおりです。
@interface SQLiteRelatedItemPresenter : NSObject <NSFilePresenter>
{
NSURL *primaryPresentedItemURL;
NSURL *presentedItemURL;
}
static NSOperationQueue *_presentedItemOperationQueue;
@implementation SQLiteRelatedItemPresenter
+ (void) initialize {
[super initialize];
if (_presentedItemOperationQueue == nil) {
_presentedItemOperationQueue = [[NSOperationQueue alloc] init];
}
}
+ (void)addPresentersForURL:(NSURL *)databaseURL {
SQLiteRelatedItemPresenter *p1, *p2, *p3, *p4;
p1 =[[SQLiteRelatedItemPresenter alloc] initWithFileURL:databaseURL prefix:nil suffix:@"-wal"];
[NSFileCoordinator addFilePresenter:p1];
p2 = [[SQLiteRelatedItemPresenter alloc] initWithFileURL:databaseURL prefix:nil suffix:@"-shm"];
[NSFileCoordinator addFilePresenter:p2];
p3 = [[SQLiteRelatedItemPresenter alloc] initWithFileURL:databaseURL prefix:nil suffix:@"-journal"];
[NSFileCoordinator addFilePresenter:p3];
p4 = [[SQLiteRelatedItemPresenter alloc] initWithFileURL:databaseURL prefix:@"." suffix:@"-conch"];
[NSFileCoordinator addFilePresenter:p4];
// +filePresenters will only return once the asynchronously added file presenters are done being registered
[NSFileCoordinator filePresenters];
}
- initWithFileURL:(NSURL *)fileURL prefix:(NSString *)prefix suffix:(NSString *)suffix {
self = [super init];
if (self) {
primaryPresentedItemURL = fileURL;
NSString *path = [fileURL path];
if (prefix) {
NSString *name = [path lastPathComponent];
NSString *dir = [path stringByDeletingLastPathComponent];
path = [dir stringByAppendingPathComponent:[prefix stringByAppendingString:name]];
}
if (suffix) {
path = [path stringByAppendingString:suffix];
}
presentedItemURL = [NSURL fileURLWithPath:path];
}
return self;
}
- (NSURL *)presentedItemURL {
return presentedItemURL;
}
- (NSOperationQueue *)presentedItemOperationQueue {
return _presentedItemOperationQueue;
}
- (NSURL *)primaryPresentedItemURL {
return primaryPresentedItemURL;
}
この特定の例は、すべて openSQLiteFileAtURL メソッド内で実行される sqlite 操作に対して正常に機能します。ロジックをサブメソッドに分割しようとすると、多くの問題が発生します。たとえば、次のようになります。
- openSQLiteFileAtURL:(NSURL *)databaseURL; // just open the db
- executeSQLStatement:(NSString *)sql; //perform read/write operations into the sqlite db previously opened in the openSQLiteFileAtURL method
- closeSQLite(); //close db method.
addPresentersForURL は (openSQLiteFileAtURL で) 1 回だけ呼び出す必要があるようですが、サンドボックスの特権エラーが原因でアプリを動作させることができませんでした...助けはありますか?