简介:是iOS/macOS提供的轻量级键值持久化,适合存储用户偏好、小配置、状态标记等简单数据(不适合大体积数据活着复杂查询)。字符串、数字、数组、字典、日期、NSData,如果需要存储自定义对象,需要先把对象序列化为NSDAta(前面在讲解多界面传值有涉及)
使用示例:
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
// 写
[defs setObject:@"Mike" forKey:@"username"];
[defs setInteger:28 forKey:@"age"];
[defs setBool:YES forKey:@"isLogin"];
/*
数据会先存在内存里,系统会在适当时机自动写到
*/
// 读
NSString *name = [defs stringForKey:@"username"];
NSInteger age = [defs integerForKey:@"age"];
BOOL isLogin = [defs boolForKey:@"isLogin"];
// 删除某个键
[defs removeObjectForKey:@"username"];
不需要每次都调用synchronize方法(会强制立刻将内存的数据写入磁盘),避免频繁调用导致卡顿耗电增加,影响性能
注册默认值(fallback/注册域)
如果需要给没有存储过的键提供默认值,用registerDefaults:方法(在 app 启动处(application:didFinishLaunchingWithOptions:
)注册默认值:)
NSDictionary *defaults = @{ @"theme": @"light", @"pageSize": @20 };
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
/*这是只读回退,不会覆盖已有值,也不会写入磁盘*/
监听变化
当别的地方更改了偏好设置,我们想要自动更新UI时
// 注册监听(比如在 viewDidLoad)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(defaultsChanged:)
name:NSUserDefaultsDidChangeNotification
object:nil];
- (void)defaultsChanged:(NSNotification *)note {
//这个通知不知道哪个key变了,需要自己再去读
BOOL dark = [[NSUserDefaults standardUserDefaults] boolForKey:@"darkModeEnabled"];
NSLog(@"偏好变了,darkMode=%d", dark);
}
如何找到是哪一个key改变了
这里我们不要直接使用系统自带的set方法存数据,我们自己实现一个存数据的方法,告诉外面改变的是哪一个key
1.我们写一个存的方法
- (void)mySetValue:(id)value forKey:(NSString *)key {
[[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
[[NSNotificationCenter defaultCenter] postNotificationName:@"note"
object:nil
userInfo:@{@"key": key}];//告诉别人我们改变的是哪一个key
}
2.在需要知道变化的地方监听这个通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(myKeyChanged:)
name:@"note"
object:nil];
3.在回调方法中取出key
- (void)myKeyChanged:(NSNotification *)note {
NSString *changedKey = note.userInfo[@"key"];
NSLog(@"有人改了 key=%@", changedKey);
}
存储自定义对象
除了在多界面传值那里的解当归档方法,这里再介绍使用JSON实现
NSDictionary *profile = @{@"name": @"Alice", @"age": @20};
NSData *json = [NSJSONSerialization dataWithJSONObject:profile options:0 error:nil];
[[NSUserDefaults standardUserDefaults] setObject:json forKey:@"profile"];
// 取出
NSData *saved = [[NSUserDefaults standardUserDefaults] objectForKey:@"profile"];
if (saved) {
NSDictionary *obj = [NSJSONSerialization JSONObjectWithData:saved options:0 error:nil];
NSLog(@"%@", obj);
}
获取所有键
// 打印所有
NSDictionary *all = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
NSLog(@"所有键值: %@", all);
// 清空当前 App 的持久域(慎用)
NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:bundleID];
拓展:
第一次访问某个域的时候,系统会把对应的plist加载到内存,后续读取都是查内存缓存,所以非常快。如果我们在同一进程里改值,那么再读取就是最新的,如果是跨进程,由于各自都有缓存,存在短暂不同步的窗口,但是并不影响