2013-02-26 54 views
7

我一直在試驗CLLocationManagerstartMonitoringSignificantLocationChanges,我遇到了一些與Core Data有關的問題。事實證明,自iOS 5.0起,核心數據默認使用NSFileProtectionCompleteUntilFirstUserAuthentication。這意味着如果設置了密碼,持久性存儲在設備開機之前不可用,直到第一次輸入密碼時爲止。如果您使用的是位置更新,那麼您的應用可能會在此期間啓動,而Core Data在嘗試加載持久存儲時會出現錯誤。處理背景位置更新和核心數據文件保護

顯然切換到NSFileProtectionNone將是解決這個問題的最簡單的方法。我寧願不要 - 雖然我沒有在數據庫中存儲任何超級敏感的東西,但是這些位置更新也不是超級重要的。

我知道我可以使用​​來檢查數據是否已解鎖,並且我可以在我的應用程序委託中使用applicationProtectedDataWillBecomeUnavailable:,以便在解鎖後進行相應的響應。這對我來說似乎很混亂 - 我必須添加一堆額外的檢查,以確保如果持久性存儲不可用,在一旦它變爲可用狀態後重新設置一堆事物等等,以確保沒有任何問題。而且額外的代碼並沒有提供太多的好處 - 如果應用程序在此狀態下啓動,該應用程序仍然無法執行任何操作。

所以我想我只是不知道這是更「正確」的方式來處理這個:

  1. 切換到NSFileProtectionNone
  2. 如果商店不可用,請添加額外支票以跳過這些內容,並使用applicationProtectedDataWillBecomeUnavailable:重新設置它。
  3. 如果應用程序在後臺啓動([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground)並且受保護的數據不可用([[UIApplication sharedApplication] isProtectedDataAvailable] == NO)),請致電exit(0)(或類似的東西)退出應用程序。一方面,這似乎是最簡單的解決方案,而我並沒有看到任何缺點。但它似乎......「錯誤」?我想我不能決定它是一個乾淨的解決方案還是隻是一個懶惰的解決方案。
  4. 別的東西我只是沒有想到?

回答

4

想了一會兒,我想出了一個解決方案,我很滿意。 exit(0)選項需要考慮的一件事是,如果用戶需要一段時間解鎖設備,應用程序可能會不斷加載,退出和重新加載。而如果你只是阻止應用程序做了很多事情,它可能只需要加載一次,而且很可能會更有效率。所以我決定試試我的選項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."]; 
} 

這樣就完成了安裝和重新啓用接口。這是大部分工作,但您還需要檢查其他代碼,以確保在持久存儲可用之前沒有任何依賴於核心數據的調用。有一點需要注意的是,如果用戶從此後臺狀態啓動應用程序,applicationWillEnterForegroundapplicationDidBecomeActive可能會在applicationProtectedDataDidBecomeAvailable之前調用。因此,我在不同的地方添加了0​​以確保在準備就緒之前不會有任何運行。一旦數據庫被加載,我還有幾個需要刷新界面的地方。

爲了(部分)測試這個沒有很多開車過來的,我臨時修改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:。這樣我可以啓動應用程序,確保沒有意外發生,按主頁按鈕,再次打開應用程序,並確保一切正常。由於實際的代碼需要移動很長的距離並且每次等待五分鐘,這給了我一個很好的方式來估計發生的事情。

絆倒我的最後一件事是我的持續性店鋪協調員。一個典型的實現可能是這個樣子:

- (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; 
} 

這是鬆散的基礎上蘋果的示例代碼,這並在你需要適當處理錯誤註釋解釋。我自己的代碼做的不僅僅是這一點,但有一件事我沒有考慮到,如果加載持久存儲時出錯,這將返回非零結果!這是讓我所有的其他代碼繼續進行,就好像它正常工作。即使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; 
} 
+0

@robertspacer 不會_persistentStoreCoordinator是nil上這一行: 如果([_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType配置:無URL:storeURL選項:無誤差:&錯誤]){ – 2013-04-05 22:43:12

+0

@GlenŤ是的,這應該一直是協調員的參考。固定。 – robotspacer 2013-04-08 23:59:48

0

我都經歷過,你必須檢查

[[UIApplication sharedApplication] isProtectedDataAvailable] 

和工藝

applicationProtectedDataWillBecomeUnavailable 

,以確保你不訪問受保護的文件。 檢查

managedObjectContext 

沒有爲我工作。