数据持久化方案一----NSUserDefaults(偏好设置)

一、NSUserDefaults是专门用来存储App配置信息的,一般不要在偏好设置中保存其他数据。是一个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数据库中其实是由多个层级的域组成的,当你读取一个键值的数据时,NSUserDefaults从上到下透过域的层级寻找正确的值,不同的域有不同的功能,有些域是可持久的,有些域则不行。默认包含五个Domain,如下:
1、参数域( argument domain)有最高优先权
2、应用域(application domain)是最重要的域,它存储着你app通过NSUserDefaults set...forKey添加的设置
3、全局域(global domain)则存储着系统的设置
4、语言域(language-specific domains)则包括地区、日期等
5、注册域(registration domain)仅有较低的优先权,只有在应用域没有找到值时才从注册域去寻找
例如,当某个界面是第一次运行时,这时的key值(键)所对应的值还不存在,所以NSUserDefaults对象会使用默认值为0。但是对于某些设置,可能需要临时的、非0的默认值(如"出厂设置"),否则应用将无法正确的运行。一般默认值都是存放于注册域中。默认情况下,应用域是空的,没有键也没有值。当用户第一次修改某个设置时,相应的值会通过指定的键加入应用域。当通过NSUserDefaults对象获取某个设置的值时,该对象先会在应用域查找,如果没有,NSUserDefaults则会在注册域中查找并返回默认值。查询顺序:参数域 -> 应用域 -> 全局域 -> 语言域 ->注册域。

五、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方法,如果没有调用,系统会根据I/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));

//删(同样调用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;

返回包含搜索列表中域中所有键值对的联合的字典

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


//维护持久域
//返回当前持久域名的数组(不推荐使用
- (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;

//访问托管环境密钥

//返回一个布尔值,该值指示指定密钥是否由管理员管理
- (BOOL)objectIsForcedForKey:(NSString *)key;
//返回一个布尔值,该值指示指定域中的密钥是否由管理员管理
- (BOOL)objectIsForcedForKey:(NSString *)key inDomain:(NSString *)domain;

//域

//由所有应用程序所看到的默认值组成的域(全局域)
FOUNDATION_EXPORT NSString * const NSGlobalDomain;
//从应用程序的参数解析默认值组成的域(参数域)
FOUNDATION_EXPORT NSString * const NSArgumentDomain;
//由一组临时缺省值组成的域,其值可以由应用程序设置,以确保搜索始终是成功的(注册域)
FOUNDATION_EXPORT NSString * const NSRegistrationDomain;

//相关通知

//当用户默认数据中存储更多数据时
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);

//发布时,默认完成下载数据,无论是第一次设备连接到一个iCloud帐户,或当用户切换他们的主要iCloud帐户
FOUNDATION_EXPORT NSNotificationName const NSUbiquitousUserDefaultsCompletedInitialSyncNotification API_AVAILABLE(ios(9.3), watchos(2.0), tvos(9.0)) API_UNAVAILABLE(macos);

//在当前进程中更改用户默认值时发布
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方式的持久化出问题了。

经过实际测试如果存储后立即退出或者意外闪退,NSUserDefaults的数据会丢失。
翻看文档是异步存储:

/*!
   -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中已经弃用

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

建议换个轻量型数据库处理吧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容