10

私はを実験してきましたが、 CLLocationManagerCoreDatastartMonitoringSignificantLocationChangesでいくつかの問題が発生しました。iOS 5.0以降、CoreDataはデフォルトでを使用していることがわかりましたNSFileProtectionCompleteUntilFirstUserAuthentication。これは、パスコードが設定されている場合、デバイスの電源を入れてからパスコードを最初に入力するまで、永続ストアを使用できないことを意味します。位置情報の更新を使用している場合、その間にアプリが起動する可能性があり、永続ストアを読み込もうとするとCoreDataでエラーが発生します。

NSFileProtectionNone明らかに、これを解決する最も簡単な方法はに切り替えることです。ただし、データベースには非常に機密性の高いものは何も保存していませんが、これらの場所の更新もそれほど重要ではありません。

[[UIApplication sharedApplication] isProtectedDataAvailable]データがまだロック解除されているかどうかを確認するために使用できることはわかっていますapplicationProtectedDataWillBecomeUnavailable:。また、アプリケーションデリゲートを使用して、ロックが解除されたら適切に応答できます。ただし、これは面倒に思えます。永続ストアが利用できない場合に問題が発生しないことを確認するための追加のチェックを追加したり、利用可能になったら一連の設定を再設定したりする必要があります。そして、その余分なコードはあまりメリットがありません。この状態で起動した場合、アプリはまだ何もできません。

だから私はこれに対処するためのより「適切な」方法がどれであるかわからないと思います:

  1. に切り替えますNSFileProtectionNone
  2. ストアが利用できない場合はスキップするための追加のチェックを追加し、利用できなくなったらそれを使用applicationProtectedDataWillBecomeUnavailable:して再度設定します。
  3. アプリがバックグラウンドで起動され([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground)、保護されたデータが利用できない場合( )は、(または同様の)[[UIApplication sharedApplication] isProtectedDataAvailable] == NO)電話をかけてアプリを終了します。exit(0)一方で、これは最も単純な解決策のように思えますが、実際にはマイナス面は見られません。しかし、それも…「間違っている」ように思われますか?クリーンなソリューションなのか、怠惰なソリューションなのか判断できないと思います。
  4. 私が考えていない他の何か?
4

2 に答える 2

6

これをしばらく考えた後、満足のいく解決策を思いつきました。このオプションで考慮すべきことの 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 つは、ユーザーがこのバックグラウンド状態からアプリを起動すると、前にapplicationWillEnterForegroundapplicationDidBecomeActiveが呼び出される可能性があることです。そのため、準備が整う前に何も実行されないよう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;
}
于 2013-03-04T20:37:46.793 に答える
0

私はあなたがチェックしなければならないことを経験しました

[[UIApplication sharedApplication] isProtectedDataAvailable]

とプロセス

applicationProtectedDataWillBecomeUnavailable

保護されたファイルにアクセスしないようにします。チェック中

managedObjectContext

私にはうまくいきませんでした。

于 2015-12-07T09:39:47.413 に答える