これをしばらく考えた後、満足のいく解決策を思いつきました。このオプションで考慮すべきことの 1 つexit(0)
は、ユーザーがデバイスのロックを解除するのに時間がかかる場合、アプリが継続的に読み込み、終了、再読み込みを行う可能性があることです。一方、単純にアプリが多くのことを実行できないようにすると、おそらく 1 回の読み込みで済み、より効率的になる可能性が高くなります。そこで、オプション 3 を試してみることにしました。思ったより簡単にできました。
最初にBOOL setupComplete
、アプリのデリゲートにプロパティを追加しました。これにより、アプリがさまざまな時点で完全に起動されたかどうかを簡単に確認できます。次にapplication:didFinishLaunchingWithOptions:
、管理対象オブジェクト コンテキストの初期化を試みてから、次のようにします。
NSManagedObjectContext *moc = [self managedObjectContext];
if (moc) {
self.setupComplete = YES;
[self setupWithManagedObjectContext:moc];
} else {
UIApplication *app = [UIApplication sharedApplication];
if ([app applicationState] == UIApplicationStateBackground && ![app isProtectedDataAvailable]) {
[app beginIgnoringInteractionEvents];
} else [self presentErrorWithTitle:@"There was an error opening the database."];
}
setupWithManagedObjectContext:
セットアップを完了するカスタム メソッドです。が必要かどうかはわかりませんbeginIgnoringInteractionEvents
が、安全のために追加しました。そうすれば、アプリが前面に表示されたときに、セットアップが完了するまでインターフェースが固定されていることを確認できます. 熱心なユーザーが心配そうにタップしている場合、クラッシュを回避できる可能性があります。
次に、applicationProtectedDataDidBecomeAvailable:
次のように呼び出します。
if (!self.setupComplete) {
NSManagedObjectContext *moc = [self managedObjectContext];
if (moc) {
self.setupComplete = YES;
[self setupWithManagedObjectContext:moc];
UIApplication *app = [UIApplication sharedApplication];
if ([app isIgnoringInteractionEvents]) [app endIgnoringInteractionEvents];
} else [self presentErrorWithTitle:@"There was an error opening the database."];
}
これでセットアップが完了し、インターフェイスが再び有効になります。これがほとんどの作業ですが、永続ストアが使用可能になる前に Core Data に依存するものが呼び出されていないことを確認するために、他のコードもチェックする必要があります。注意すべきことの 1 つは、ユーザーがこのバックグラウンド状態からアプリを起動すると、前にapplicationWillEnterForeground
とapplicationDidBecomeActive
が呼び出される可能性があることです。そのため、準備が整う前に何も実行されないようapplicationProtectedDataDidBecomeAvailable
に、さまざまな場所に追加しました。if (self.setupComplete) { … }
また、データベースのロード後にインターフェイスを更新する必要がある場所がいくつかありました。
これを(部分的に)テストするために、多くの運転をせずにapplication:didFinishLaunchingWithOptions:
、データベースをセットアップしないように一時的に変更しました。
NSManagedObjectContext *moc = nil; // [self managedObjectContext];
if (moc) {
self.setupComplete = YES;
[self setupWithManagedObjectContext:moc];
} else {
UIApplication *app = [UIApplication sharedApplication];
// if ([app applicationState] == UIApplicationStateBackground && ![app isProtectedDataAvailable]) {
[app beginIgnoringInteractionEvents];
// } else [self presentErrorWithTitle:@"There was an error opening the database."];
}
次に、コードを に移動しapplicationProtectedDataDidBecomeAvailable:
ましたapplicationWillEnterForeground:
。そうすれば、アプリを起動し、予期しないことが起こらないことを確認し、ホームボタンを押してアプリを再度開き、すべてが機能していることを確認できました. 実際のコードではかなりの距離を移動し、毎回 5 分間待機する必要があるため、これにより何が起こっているかを概算することができました。
最後に私をつまずかせたのは、しつこい店のコーディネーターでした。典型的な実装は次のようになります。
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) return _persistentStoreCoordinator;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Test.sqlite"];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
return _persistentStoreCoordinator;
}
これは大まかに、エラーを適切に処理する必要があることをコメントで説明している Apple のサンプル コードに基づいています。私自身のコードはこれよりも少し多くのことを行いますが、永続ストアのロード中にエラーが発生した場合、nil 以外の結果が返されるとは考えていませんでした。これにより、他のすべてのコードが正しく機能しているかのように処理できるようになりました。また、persistentStoreCoordinator が再度呼び出された場合でも、ストアを再度ロードしようとするのではなく、有効なストアなしで同じコーディネーターを返すだけでした。これに対処するにはさまざまな方法がありますが、ストアを追加できない限り、_persistentStoreCoordinator を設定しないのが最善のように思えました。
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) return _persistentStoreCoordinator;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Test.sqlite"];
NSError *error = nil;
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if ([coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
_persistentStoreCoordinator = coordinator;
} else {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
return _persistentStoreCoordinator;
}