數(shù)據(jù)持久化方案一----NSUserDefaults(偏好設置)

一、NSUserDefaults是專門用來存儲App配置信息的,一般不要在偏好設置中保存其他數(shù)據(jù)。是一個plist,名字為Bundle identifier名字,如cn.com.xiaoli.XLDemo.plist

二、存儲位置
沙盒--> Library-->Preferences: *.plist

NSString *preferencePath = NSSearchPathForDirectoriesInDomains(NSPreferencePanesDirectory, NSUserDomainMask, YES).firstObject;

三、支持持久化類型
NSArray(含NSMutableArray)、NSDictionary(含NSMutableDictionary)、NSData(含NSMutableData)、NSString(含NSMutableString)、NSNumber、NSDate、BOOL。但存儲的對象全是不可變的。

四、NSUserDefaults的域:
UserDefaults數(shù)據(jù)庫中其實是由多個層級的域組成的,當你讀取一個鍵值的數(shù)據(jù)時,NSUserDefaults從上到下透過域的層級尋找正確的值,不同的域有不同的功能,有些域是可持久的,有些域則不行。默認包含五個Domain,如下:
1、參數(shù)域( argument domain)有最高優(yōu)先權
2、應用域(application domain)是最重要的域,它存儲著你app通過NSUserDefaults set...forKey添加的設置
3、全局域(global domain)則存儲著系統(tǒng)的設置
4、語言域(language-specific domains)則包括地區(qū)、日期等
5、注冊域(registration domain)僅有較低的優(yōu)先權,只有在應用域沒有找到值時才從注冊域去尋找
例如,當某個界面是第一次運行時,這時的key值(鍵)所對應的值還不存在,所以NSUserDefaults對象會使用默認值為0。但是對于某些設置,可能需要臨時的、非0的默認值(如"出廠設置"),否則應用將無法正確的運行。一般默認值都是存放于注冊域中。默認情況下,應用域是空的,沒有鍵也沒有值。當用戶第一次修改某個設置時,相應的值會通過指定的鍵加入應用域。當通過NSUserDefaults對象獲取某個設置的值時,該對象先會在應用域查找,如果沒有,NSUserDefaults則會在注冊域中查找并返回默認值。查詢順序:參數(shù)域 -> 應用域 -> 全局域 -> 語言域 ->注冊域。

五、api使用方法(類似NSMutableDictionary)

//獲取標準用戶默認對象(配置為搜索當前應用程序搜索列表的NSUserDefaults的全局實例)
@property (class, readonly, strong) NSUserDefaults *standardUserDefaults;
//重置
+ (void)resetStandardUserDefaults;
// 用應用程序和當前用戶的默認值初始化用戶默認對象
- (nullable instancetype)initWithSuiteName:(nullable NSString *)suitename API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
- (nullable id)initWithUser:(NSString *)username API_DEPRECATED("Use -init instead", macos(10.0,10.9), ios(2.0,7.0), watchos(2.0,2.0), tvos(9.0,9.0));


//存
- (void)setObject:(nullable id)value forKey:(NSString *)defaultName;
- (void)setInteger:(NSInteger)value forKey:(NSString *)defaultName;
- (void)setFloat:(float)value forKey:(NSString *)defaultName;
- (void)setDouble:(double)value forKey:(NSString *)defaultName;
- (void)setBool:(BOOL)value forKey:(NSString *)defaultName;
- (void)setURL:(nullable NSURL *)url forKey:(NSString *)defaultName API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

存完后要調用synchronize方法,如果沒有調用,系統(tǒng)會根據(jù)I/O情況不定時刻地保存到文件中,會造成數(shù)據(jù)丟失。所以如果需要立即寫入文件的就必須調用synchronize方法。

//取
- (nullable id)objectForKey:(NSString *)defaultName;
- (nullable NSString *)stringForKey:(NSString *)defaultName;
- (nullable NSArray *)arrayForKey:(NSString *)defaultName;
- (nullable NSDictionary<NSString *, id> *)dictionaryForKey:(NSString *)defaultName;
- (nullable NSData *)dataForKey:(NSString *)defaultName;
- (nullable NSArray<NSString *> *)stringArrayForKey:(NSString *)defaultName;
- (NSInteger)integerForKey:(NSString *)defaultName;
- (float)floatForKey:(NSString *)defaultName;
- (BOOL)boolForKey:(NSString *)defaultName;
- (nullable NSURL *)URLForKey:(NSString *)defaultName API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

//刪(同樣調用synchronize方法)
// -removeObjectForKey: is equivalent to -[... setObject:nil forKey:defaultName]
- (void)removeObjectForKey:(NSString *)defaultName;

注冊默認值

//將指定字典的內容添加到注冊域
- (void)registerDefaults:(NSDictionary<NSString *, id> *)registrationDictionary;

//維護套間

//將指定的域名插入到接收方的搜索列表中
- (void)addSuiteNamed:(NSString *)suiteName;
//從接收器的搜索列表中移除指定的域名
- (void)removeSuiteNamed:(NSString *)suiteName;

返回包含搜索列表中域中所有鍵值對的聯(lián)合的字典

- (NSDictionary<NSString *, id> *)dictionaryRepresentation;

維護易失性域

//易失性域名
@property (readonly, copy) NSArray<NSString *> *volatileDomainNames;
//返回指定的易失性域的字典
- (NSDictionary<NSString *, id> *)volatileDomainForName:(NSString *)domainName;
//為指定的易失性域設置字典
- (void)setVolatileDomain:(NSDictionary<NSString *, id> *)domain forName:(NSString *)domainName;
//從用戶的默認值中移除指定的易失性域
- (void)removeVolatileDomainForName:(NSString *)domainName;


//維護持久域
//返回當前持久域名的數(shù)組(不推薦使用
- (NSArray *)persistentDomainNames API_DEPRECATED("Not recommended", macos(10.0,10.9), ios(2.0,7.0), watchos(2.0,2.0), tvos(9.0,9.0));
//返回指定域默認值的字典表示形式
- (nullable NSDictionary<NSString *, id> *)persistentDomainForName:(NSString *)domainName;
//為指定的持久域設置字典
- (void)setPersistentDomain:(NSDictionary<NSString *, id> *)domain forName:(NSString *)domainName;
//從用戶的默認值中移除指定的持久域的內容
- (void)removePersistentDomainForName:(NSString *)domainName;

//訪問托管環(huán)境密鑰

//返回一個布爾值,該值指示指定密鑰是否由管理員管理
- (BOOL)objectIsForcedForKey:(NSString *)key;
//返回一個布爾值,該值指示指定域中的密鑰是否由管理員管理
- (BOOL)objectIsForcedForKey:(NSString *)key inDomain:(NSString *)domain;

//域

//由所有應用程序所看到的默認值組成的域(全局域)
FOUNDATION_EXPORT NSString * const NSGlobalDomain;
//從應用程序的參數(shù)解析默認值組成的域(參數(shù)域)
FOUNDATION_EXPORT NSString * const NSArgumentDomain;
//由一組臨時缺省值組成的域,其值可以由應用程序設置,以確保搜索始終是成功的(注冊域)
FOUNDATION_EXPORT NSString * const NSRegistrationDomain;

//相關通知

//當用戶默認數(shù)據(jù)中存儲更多數(shù)據(jù)時
FOUNDATION_EXPORT NSNotificationName const NSUserDefaultsSizeLimitExceededNotification API_AVAILABLE(ios(9.3), watchos(2.0), tvos(9.0)) API_UNAVAILABLE(macos);

//當設置了云默認時,但沒有登錄iCloud用戶
FOUNDATION_EXPORT NSNotificationName const NSUbiquitousUserDefaultsNoCloudAccountNotification API_AVAILABLE(ios(9.3), watchos(2.0), tvos(9.0)) API_UNAVAILABLE(macos);

//當用戶更改主iCloud帳戶時
FOUNDATION_EXPORT NSNotificationName const NSUbiquitousUserDefaultsDidChangeAccountsNotification API_AVAILABLE(ios(9.3), watchos(2.0), tvos(9.0)) API_UNAVAILABLE(macos);

//發(fā)布時,默認完成下載數(shù)據(jù),無論是第一次設備連接到一個iCloud帳戶,或當用戶切換他們的主要iCloud帳戶
FOUNDATION_EXPORT NSNotificationName const NSUbiquitousUserDefaultsCompletedInitialSyncNotification API_AVAILABLE(ios(9.3), watchos(2.0), tvos(9.0)) API_UNAVAILABLE(macos);

//在當前進程中更改用戶默認值時發(fā)布
FOUNDATION_EXPORT NSNotificationName const NSUserDefaultsDidChangeNotification;

例子:

//1.獲得NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中寫入內容
[userDefaults setObject:@"qwert" forKey:@"testKey"];
//2.1立即同步
[userDefaults synchronize];
//3.讀取文件
NSString *testKey = [userDefaults objectForKey:@"testKey"];
NSLog(@"%@", testKey);
//4.刪除文件
[userDefaults removeObjectForKey:@"testKey"];
[userDefaults synchronize];

五、思考與棄用
項目中的一些簡易信息是使用NSUserDefaults存的,最近收到用戶反饋之前存的東西找不到了,檢查了代碼很多次也沒有查出問題,便懷疑NSUserDefaults方式的持久化出問題了。

經(jīng)過實際測試如果存儲后立即退出或者意外閃退,NSUserDefaults的數(shù)據(jù)會丟失。
翻看文檔是異步存儲:

/*!
   -setObject:forKey: immediately stores a value (or removes the value if nil is passed as the value) for the             provided key in the search list entry for the receiver's suite name in the current user and any host, then asynchronously stores the value persistently, where it is made available to other processes.
 */
  - (void)setObject:(nullable id)value forKey:(NSString *)defaultName;

//舊版本中的強制同步在iOS14中不建議用,iOS15中已經(jīng)棄用

  /*!
   -synchronize is deprecated and will be marked with the API_DEPRECATED macro in a future release.

   -synchronize blocks the calling thread until all in-progress set operations have completed. This is no longer necessary. Replacements for previous uses of -synchronize depend on what the intent of calling synchronize was. If you synchronized...
   - ...before reading in order to fetch updated values: remove the synchronize call
   - ...after writing in order to notify another program to read: the other program can use KVO to observe the default without needing to notify
   - ...before exiting in a non-app (command line tool, agent, or daemon) process: call CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication)
   - ...for any other reason: remove the synchronize call
   */
- (BOOL)synchronize;

建議換個輕量型數(shù)據(jù)庫處理吧

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容