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

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

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

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

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

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

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

//獲取標(biāo)準(zhǔn)用戶默認(rèn)對象(配置為搜索當(dāng)前應(yīng)用程序搜索列表的NSUserDefaults的全局實(shí)例)
@property (class, readonly, strong) NSUserDefaults *standardUserDefaults;
//重置
+ (void)resetStandardUserDefaults;
// 用應(yīng)用程序和當(dāng)前用戶的默認(rèn)值初始化用戶默認(rèn)對象
- (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));

存完后要調(diào)用synchronize方法,如果沒有調(diào)用,系統(tǒng)會(huì)根據(jù)I/O情況不定時(shí)刻地保存到文件中,會(huì)造成數(shù)據(jù)丟失。所以如果需要立即寫入文件的就必須調(diào)用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));

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

注冊默認(rèn)值

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

//維護(hù)套間

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

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

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

維護(hù)易失性域

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


//維護(hù)持久域
//返回當(dāng)前持久域名的數(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));
//返回指定域默認(rèn)值的字典表示形式
- (nullable NSDictionary<NSString *, id> *)persistentDomainForName:(NSString *)domainName;
//為指定的持久域設(shè)置字典
- (void)setPersistentDomain:(NSDictionary<NSString *, id> *)domain forName:(NSString *)domainName;
//從用戶的默認(rèn)值中移除指定的持久域的內(nèi)容
- (void)removePersistentDomainForName:(NSString *)domainName;

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

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

//域

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

//相關(guān)通知

//當(dāng)用戶默認(rèn)數(shù)據(jù)中存儲(chǔ)更多數(shù)據(jù)時(shí)
FOUNDATION_EXPORT NSNotificationName const NSUserDefaultsSizeLimitExceededNotification API_AVAILABLE(ios(9.3), watchos(2.0), tvos(9.0)) API_UNAVAILABLE(macos);

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

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

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

//在當(dāng)前進(jìn)程中更改用戶默認(rèn)值時(shí)發(fā)布
FOUNDATION_EXPORT NSNotificationName const NSUserDefaultsDidChangeNotification;

例子:

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

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

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

/*!
   -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;

//舊版本中的強(qiáng)制同步在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;

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

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

相關(guān)閱讀更多精彩內(nèi)容

  • 關(guān)于首選項(xiàng)和設(shè)置 首選項(xiàng)是您持久存儲(chǔ)的信息,并用于配置您的應(yīng)用程序。應(yīng)用程序通常會(huì)向用戶公開偏好設(shè)置,以便他們自定...
    nicedayCoco閱讀 1,153評論 0 0
  • 概述: NSUserDefaults,官網(wǎng)上的定義是一個(gè)用戶默認(rèn)數(shù)據(jù)庫的接口,在應(yīng)用程序的啟動(dòng)過程中,持久地存儲(chǔ)鍵...
    淺_若清風(fēng)閱讀 7,434評論 1 9
  • 文件 歸檔(NSKeyedArchiver) 屬性列表(NSUserDefaults) 數(shù)據(jù)庫(SQLite、Co...
    零度_不結(jié)冰閱讀 8,715評論 0 2
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,619評論 1 32
  • 羅輯思維 羅振宇說成年人學(xué)習(xí)要注意5個(gè)關(guān)鍵詞:人格、概念、縫合、碎片、目標(biāo)。 還舉個(gè)例子,一個(gè)成年人想學(xué)編程,買了...
    小殊閱讀 121評論 0 0

友情鏈接更多精彩內(nèi)容