iOS OC NSUserDefaults
文章目录
- [iOS OC NSUserDefaults](#iOS OC NSUserDefaults)
-
- [一、NSUserDefaults 到底用来干什么?](#一、NSUserDefaults 到底用来干什么?)
- 二、使用指南
-
- [2.1 八大类型 存储方法](#2.1 八大类型 存储方法)
- [2.2 八大类型 读取方法](#2.2 八大类型 读取方法)
- [2.3 删除数据](#2.3 删除数据)
- [2.4 拓展方法](#2.4 拓展方法)
- [三、数组 / 字典 进阶操作](#三、数组 / 字典 进阶操作)
-
- [3.1 搜索历史累加存储(动态新增、去重、限制10条)](#3.1 搜索历史累加存储(动态新增、去重、限制10条))
- [3.2 清空搜索历史](#3.2 清空搜索历史)
- [3.3 字典局部更新(不用整体覆盖)](#3.3 字典局部更新(不用整体覆盖))
- [四、自定义 Model 存储](#四、自定义 Model 存储)
-
- [4.1 模型协议配置](#4.1 模型协议配置)
- [4.2 模型存储 & 读取代码](#4.2 模型存储 & 读取代码)
- 五、高频场景
-
- [场景1:判断首次启动 & 展示引导页](#场景1:判断首次启动 & 展示引导页)
- [场景2:用户登录状态保存 & 退出登录清空](#场景2:用户登录状态保存 & 退出登录清空)
- 六、项目避坑
-
- 坑1:读取空值不容错,页面空数据UI错乱
- [坑2:Key 硬编码,拼写错误、重复覆盖](#坑2:Key 硬编码,拼写错误、重复覆盖)
- [坑3:存 nil 等于删除数据](#坑3:存 nil 等于删除数据)
- 坑4:数组字典嵌套自定义对象,直接闪退
- [坑5:循环频繁 set 数据,造成卡顿](#坑5:循环频繁 set 数据,造成卡顿)
- 坑6:重要数据不同步,闪退丢失数据
- 七、完整工具类
一、NSUserDefaults 到底用来干什么?
所有小体量、非敏感、配置型、标记型数据,全部用 NSUserDefaults。
项目标准使用场景:
- 用户登录状态、游客模式状态
- App 深色/浅色/跟随系统主题
- 是否首次启动、是否展示过引导页
- 弹窗是否已展示、功能新手提示是否关闭
- 用户偏好设置:字体、音量、自动播放、静音
- 搜索历史、筛选条件、上次选中状态
- 环境切换标记(测试/生产)、版本更新记录
- 权限弹窗不再提示标记
项目禁止场景:
- 密码等敏感数据
- 大量数据
二、使用指南
先统一获取单例(项目唯一标准写法,禁止 alloc init)
objectivec
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
2.1 八大类型 存储方法
objectivec
// 1. 字符串存储
[defaults setObject:@"开发者" forKey:@"user_nickname"];
// 2. 整型存储
[defaults setInteger:100 forKey:@"user_level"];
// 3. 浮点型存储
[defaults setFloat:0.8f forKey:@"app_volume"];
// 4. 双精度存储
[defaults setDouble:3.1415926 forKey:@"app_config_num"];
// 5. 布尔值存储
[defaults setBool:YES forKey:@"is_dark_mode"];
// 6. 数组存储
[defaults setObject:@[@"OC",@"Swift",@"iOS"] forKey:@"search_history"];
// 7. 字典存储
[defaults setObject:@{@"font":@"15",@"autoPlay":@"1"} forKey:@"app_ui_config"];
// 8. 日期存储
[defaults setObject:[NSDate date] forKey:@"last_login_time"];
// 重要数据强制落盘
[defaults synchronize];
2.2 八大类型 读取方法
objectivec
// 字符串读取
NSString *nickName = [defaults stringForKey:@"user_nickname"];
// 整型读取
NSInteger level = [defaults integerForKey:@"user_level"];
// 浮点读取
float volume = [defaults floatForKey:@"app_volume"];
// 双精度读取
double configNum = [defaults doubleForKey:@"app_config_num"];
// 布尔读取
BOOL isDark = [defaults boolForKey:@"is_dark_mode"];
// 数组读取
NSArray *historyArr = [defaults arrayForKey:@"search_history"];
// 字典读取
NSDictionary *uiConfig = [defaults dictionaryForKey:@"app_ui_config"];
// 日期读取
NSDate *lastLoginDate = [defaults objectForKey:@"last_login_time"];
2.3 删除数据
objectivec
// 1. 删除单个指定Key
[defaults removeObjectForKey:@"user_nickname"];
// 2. 清空当前App所有UserDefaults数据
NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
[defaults removePersistentDomainForName:bundleID];
[defaults synchronize];
2.4 拓展方法
objectivec
// 判断某个Key是否存在
- (BOOL)UD_KeyExist:(NSString *)key {
return [[NSUserDefaults standardUserDefaults] objectForKey:key] != nil;
}
// 安全读取字符串(空值容错,返回空字符串,不返回nil)
- (NSString *)UD_GetString:(NSString *)key {
NSString *str = [[NSUserDefaults standardUserDefaults] stringForKey:key];
return str ? str : @"";
}
// 安全读取数组
- (NSArray *)UD_GetArray:(NSString *)key {
NSArray *arr = [[NSUserDefaults standardUserDefaults] arrayForKey:key];
return arr ? arr : @[];
}
// 安全读取字典(空值返回空字典)
- (NSDictionary *)UD_GetDict:(NSString *)key {
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] dictionaryForKey:key];
return dict ? dict : @{};
}
三、数组 / 字典 进阶操作
3.1 搜索历史累加存储(动态新增、去重、限制10条)
这是几乎所有App都有的功能,完整可直接复用代码:
objectivec
- (void)saveSearchHistory:(NSString *)keyword {
if (!keyword || keyword.length == 0) return;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *historyArr = [[defaults arrayForKey:@"search_history"] mutableCopy];
// 容错:首次为空初始化
if (!historyArr) {
historyArr = [NSMutableArray array];
}
// 去重:存在则先删除旧的
if ([historyArr containsObject:keyword]) {
[historyArr removeObject:keyword];
}
// 头部插入最新数据
[historyArr insertObject:keyword atIndex:0];
// 限制最大10条
if (historyArr.count > 10) {
[historyArr removeLastObject];
}
// 重新存储
[defaults setObject:historyArr forKey:@"search_history"];
[defaults synchronize];
}
3.2 清空搜索历史
objectivec
- (void)clearSearchHistory {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults removeObjectForKey:@"search_history"];
[defaults synchronize];
}
3.3 字典局部更新(不用整体覆盖)
objectivec
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *configDict = [[defaults dictionaryForKey:@"app_ui_config"] mutableCopy];
if (!configDict) configDict = [NSMutableDictionary dictionary];
// 只更新字体大小,其他配置保留
configDict[@"font"] = @"16";
[defaults setObject:configDict forKey:@"app_ui_config"];
[defaults synchronize];
四、自定义 Model 存储
4.1 模型协议配置
objc
// UserConfig.h
#import <Foundation/Foundation.h>
@interface UserConfig : NSObject <NSCoding>
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, assign) NSInteger userType;
@property (nonatomic, assign) BOOL autoPlay;
@end
// UserConfig.m
#import "UserConfig.h"
@implementation UserConfig
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.nickName forKey:@"nickName"];
[coder encodeInteger:self.userType forKey:@"userType"];
[coder encodeBool:self.autoPlay forKey:@"autoPlay"];
}
- (instancetype)initWithCoder:(NSDecoder *)decoder {
if (self = [super init]) {
_nickName = [decoder decodeObjectForKey:@"nickName"];
_userType = [decoder decodeIntegerForKey:@"userType"];
_autoPlay = [decoder decodeBoolForKey:@"autoPlay"];
}
return self;
}
@end
4.2 模型存储 & 读取代码
objectivec
// 存储模型
UserConfig *config = [[UserConfig alloc] init];
config.nickName = @"iOS开发者";
config.userType = 1;
config.autoPlay = YES;
NSData *configData = [NSKeyedArchiver archivedDataWithRootObject:config requiringSecureCoding:NO error:nil];
[[NSUserDefaults standardUserDefaults] setObject:configData forKey:@"user_config"];
[[NSUserDefaults standardUserDefaults] synchronize];
// 读取模型
NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:@"user_config"];
UserConfig *userConfig = [NSKeyedUnarchiver unarchivedObjectOfClass:[UserConfig class] fromData:data error:nil];
五、高频场景
场景1:判断首次启动 & 展示引导页
objectivec
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL isFirst = [defaults boolForKey:@"app_first_launch"];
if (!isFirst) {
// 展示引导页
[defaults setBool:YES forKey:@"app_first_launch"];
[defaults synchronize];
}else{
// 直接进入主页
}
场景2:用户登录状态保存 & 退出登录清空
objectivec
// 登录成功保存
[defaults setBool:YES forKey:@"user_login_status"];
[defaults setObject:@"20260607" forKey:@"user_login_time"];
// 退出登录清空
[defaults removeObjectForKey:@"user_login_status"];
[defaults removeObjectForKey:@"user_login_time"];
[defaults synchronize];
六、项目避坑
坑1:读取空值不容错,页面空数据UI错乱
问题:读取nil字符串、nil数组,直接赋值给UI会导致闪退、布局错乱。
解决:使用上面封装的安全读取方法,空值返回空字符串/空数组。
坑2:Key 硬编码,拼写错误、重复覆盖
问题:多处写死字符串key,后期维护爆炸,极易覆盖数据。
解决:全局宏定义统一管理所有Key。
objectivec
#define kAppFirstLaunch @"app_first_launch"
#define kUserLoginStatus @"user_login_status"
#define kAppDarkMode @"app_dark_mode"
坑3:存 nil 等于删除数据
很多人误以为可以存空值,实际直接删除Key,导致下次读取默认值。
objectivec
[defaults setObject:nil forKey:@"nickname"]; // 直接删除key!
坑4:数组字典嵌套自定义对象,直接闪退
容器内部只能存原生类型,嵌套Model必崩,必须归档后再存储。
坑5:循环频繁 set 数据,造成卡顿
循环内频繁读写UD,触发多次内存刷新,UI卡顿。
解决方案:循环结束后统一存储、统一同步。
坑6:重要数据不同步,闪退丢失数据
状态类、登录类、配置类重要数据,必须手动 synchronize,防止系统未自动同步导致丢失。
七、完整工具类
NSUserDefaultsTool.h
objectivec
#import <Foundation/Foundation.h>
@interface NSUserDefaultsTool : NSObject
/// 单例
+ (instancetype)shareTool;
/// 通用存储
- (void)saveValue:(id)value forKey:(NSString *)key;
/// 通用读取
- (id)getValueForKey:(NSString *)key;
/// 删除单个
- (void)removeKey:(NSString *)key;
/// 清空全部
- (void)clearAll;
/// 安全读取字符串
- (NSString *)getStringForKey:(NSString *)key;
/// 安全读取数组
- (NSArray *)getArrayForKey:(NSString *)key;
/// 安全读取字典
- (NSDictionary *)getDictForKey:(NSString *)key;
/// 读取布尔值
- (BOOL)getBoolForKey:(NSString *)key;
/// 读取整型
- (NSInteger)getIntegerForKey:(NSString *)key;
/// 存储自定义Model(归档)
- (void)saveModel:(id)model forKey:(NSString *)key;
/// 读取自定义Model(解档)
- (id)getModelWithClass:(Class)cls forKey:(NSString *)key;
@end
NSUserDefaultsTool.m
objectivec
#import "NSUserDefaultsTool.h"
@implementation NSUserDefaultsTool
+ (instancetype)shareTool {
static NSUserDefaultsTool *tool;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
tool = [[self alloc] init];
});
return tool;
}
- (void)saveValue:(id)value forKey:(NSString *)key {
if (!key) return;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (value) {
[defaults setObject:value forKey:key];
}else {
[defaults removeObjectForKey:key];
}
[defaults synchronize];
}
- (id)getValueForKey:(NSString *)key {
if (!key) return nil;
return [[NSUserDefaults standardUserDefaults] objectForKey:key];
}
- (void)removeKey:(NSString *)key {
if (!key) return;
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)clearAll {
NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:bundleID];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSString *)getStringForKey:(NSString *)key {
NSString *str = [[NSUserDefaults standardUserDefaults] stringForKey:key];
return str ? str : @"";
}
- (NSArray *)getArrayForKey:(NSString *)key {
NSArray *arr = [[NSUserDefaults standardUserDefaults] arrayForKey:key];
return arr ? arr : @[];
}
- (NSDictionary *)getDictForKey:(NSString *)key {
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] dictionaryForKey:key];
return dict ? dict : @{};
}
- (BOOL)getBoolForKey:(NSString *)key {
return [[NSUserDefaults standardUserDefaults] boolForKey:key];
}
- (NSInteger)getIntegerForKey:(NSString *)key {
return [[NSUserDefaults standardUserDefaults] integerForKey:key];
}
- (void)saveModel:(id)model forKey:(NSString *)key {
if (!model || !key) return;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:model requiringSecureCoding:NO error:nil];
[self saveValue:data forKey:key];
}
- (id)getModelWithClass:(Class)cls forKey:(NSString *)key {
NSData *data = [self getValueForKey:key];
if (!data) return nil;
return [NSKeyedUnarchiver unarchivedObjectOfClass:cls fromData:data error:nil];
}
@end
工具类项目调用示例
objectivec
// 存储字符串
[[NSUserDefaultsTool shareTool] saveValue:@"测试" forKey:@"test_str"];
// 安全读取字符串
NSString *str = [[NSUserDefaultsTool shareTool] getStringForKey:@"test_str"];
// 存储Model
[[NSUserDefaultsTool shareTool] saveModel:userConfig forKey:@"user_config"];
// 读取Model
UserConfig *config = [[NSUserDefaultsTool shareTool] getModelWithClass:[UserConfig class] forKey:@"user_config"];