iOS OC NSUserDefaults
iOS OC NSUserDefaults
文章目录
- iOS OC NSUserDefaults
- 一、NSUserDefaults 到底用来干什么?
- 二、使用指南
- 2.1 八大类型 存储方法
- 2.2 八大类型 读取方法
- 2.3 删除数据
- 2.4 拓展方法
- 三、数组 / 字典 进阶操作
- 3.1 搜索历史累加存储(动态新增、去重、限制10条)
- 3.2 清空搜索历史
- 3.3 字典局部更新(不用整体覆盖)
- 四、自定义 Model 存储
- 4.1 模型协议配置
- 4.2 模型存储 & 读取代码
- 五、高频场景
- 场景1:判断首次启动 & 展示引导页
- 场景2:用户登录状态保存 & 退出登录清空
- 六、项目避坑
- 坑1:读取空值不容错,页面空数据UI错乱
- 坑2:Key 硬编码,拼写错误、重复覆盖
- 坑3:存 nil 等于删除数据
- 坑4:数组字典嵌套自定义对象,直接闪退
- 坑5:循环频繁 set 数据,造成卡顿
- 坑6:重要数据不同步,闪退丢失数据
- 七、完整工具类
- NSUserDefaultsTool.h
- NSUserDefaultsTool.m
- 工具类项目调用示例
一、NSUserDefaults 到底用来干什么?
所有小体量、非敏感、配置型、标记型数据,全部用 NSUserDefaults。
项目标准使用场景:
- 用户登录状态、游客模式状态
- App 深色/浅色/跟随系统主题
- 是否首次启动、是否展示过引导页
- 弹窗是否已展示、功能新手提示是否关闭
- 用户偏好设置:字体、音量、自动播放、静音
- 搜索历史、筛选条件、上次选中状态
- 环境切换标记(测试/生产)、版本更新记录
- 权限弹窗不再提示标记
项目禁止场景:
- 密码等敏感数据
- 大量数据
二、使用指南
先统一获取单例(项目唯一标准写法,禁止 alloc init)
NSUserDefaults*defaults=[NSUserDefaults standardUserDefaults];2.1 八大类型 存储方法
// 1. 字符串存储[defaults setObject:@"开发者"forKey:@"user_nickname"];// 2. 整型存储[defaults setInteger:100forKey:@"user_level"];// 3. 浮点型存储[defaults setFloat:0.8fforKey:@"app_volume"];// 4. 双精度存储[defaults setDouble:3.1415926forKey:@"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 八大类型 读取方法
// 字符串读取NSString*nickName=[defaults stringForKey:@"user_nickname"];// 整型读取NSInteger level=[defaults integerForKey:@"user_level"];// 浮点读取floatvolume=[defaults floatForKey:@"app_volume"];// 双精度读取doubleconfigNum=[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 删除数据
// 1. 删除单个指定Key[defaults removeObjectForKey:@"user_nickname"];// 2. 清空当前App所有UserDefaults数据NSString*bundleID=[[NSBundle mainBundle]bundleIdentifier];[defaults removePersistentDomainForName:bundleID];[defaults synchronize];2.4 拓展方法
// 判断某个Key是否存在-(BOOL)UD_KeyExist:(NSString*)key{return[[NSUserDefaults standardUserDefaults]objectForKey:key]!=nil;}// 安全读取字符串(空值容错,返回空字符串,不返回nil)-(NSString*)UD_GetString:(NSString*)key{NSString*str=[[NSUserDefaults standardUserDefaults]stringForKey:key];returnstr?str:@"";}// 安全读取数组-(NSArray*)UD_GetArray:(NSString*)key{NSArray*arr=[[NSUserDefaults standardUserDefaults]arrayForKey:key];returnarr?arr:@[];}// 安全读取字典(空值返回空字典)-(NSDictionary*)UD_GetDict:(NSString*)key{NSDictionary*dict=[[NSUserDefaults standardUserDefaults]dictionaryForKey:key];returndict?dict:@{};}三、数组 / 字典 进阶操作
3.1 搜索历史累加存储(动态新增、去重、限制10条)
这是几乎所有App都有的功能,完整可直接复用代码:
-(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 清空搜索历史
-(void)clearSearchHistory{NSUserDefaults*defaults=[NSUserDefaults standardUserDefaults];[defaults removeObjectForKey:@"search_history"];[defaults synchronize];}3.3 字典局部更新(不用整体覆盖)
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 模型协议配置
// UserConfig.h#import<Foundation/Foundation.h>@interfaceUserConfig:NSObject<NSCoding>@property(nonatomic,copy)NSString*nickName;@property(nonatomic,assign)NSInteger userType;@property(nonatomic,assign)BOOL autoPlay;@end// UserConfig.m#import"UserConfig.h"@implementationUserConfig-(void)encodeWithCoder:(NSCoder*)coder{[coder encodeObject:self.nickName forKey:@"nickName"];[coder encodeInteger:self.userType forKey:@"userType"];[coder encodeBool:self.autoPlay forKey:@"autoPlay"];}-(instancetype)initWithCoder:(NSCoder*)decoder{if(self=[superinit]){_nickName=[decoder decodeObjectForKey:@"nickName"];_userType=[decoder decodeIntegerForKey:@"userType"];_autoPlay=[decoder decodeBoolForKey:@"autoPlay"];}returnself;}@end4.2 模型存储 & 读取代码
// 存储模型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:判断首次启动 & 展示引导页
NSUserDefaults*defaults=[NSUserDefaults standardUserDefaults];BOOL isFirst=[defaults boolForKey:@"app_first_launch"];if(!isFirst){// 展示引导页[defaults setBool:YES forKey:@"app_first_launch"];[defaults synchronize];}else{// 直接进入主页}场景2:用户登录状态保存 & 退出登录清空
// 登录成功保存[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。
#definekAppFirstLaunch@"app_first_launch"#definekUserLoginStatus@"user_login_status"#definekAppDarkMode@"app_dark_mode"坑3:存 nil 等于删除数据
很多人误以为可以存空值,实际直接删除Key,导致下次读取默认值。
[defaults setObject:nil forKey:@"nickname"];// 直接删除key!坑4:数组字典嵌套自定义对象,直接闪退
容器内部只能存原生类型,嵌套Model必崩,必须归档后再存储。
坑5:循环频繁 set 数据,造成卡顿
循环内频繁读写UD,触发多次内存刷新,UI卡顿。
解决方案:循环结束后统一存储、统一同步。
坑6:重要数据不同步,闪退丢失数据
状态类、登录类、配置类重要数据,必须手动 synchronize,防止系统未自动同步导致丢失。
七、完整工具类
NSUserDefaultsTool.h
#import<Foundation/Foundation.h>@interfaceNSUserDefaultsTool: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;@endNSUserDefaultsTool.m
#import"NSUserDefaultsTool.h"@implementationNSUserDefaultsTool+(instancetype)shareTool{staticNSUserDefaultsTool*tool;staticdispatch_once_t onceToken;dispatch_once(&onceToken,^{tool=[[selfalloc]init];});returntool;}-(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)returnnil;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];returnstr?str:@"";}-(NSArray*)getArrayForKey:(NSString*)key{NSArray*arr=[[NSUserDefaults standardUserDefaults]arrayForKey:key];returnarr?arr:@[];}-(NSDictionary*)getDictForKey:(NSString*)key{NSDictionary*dict=[[NSUserDefaults standardUserDefaults]dictionaryForKey:key];returndict?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];[selfsaveValue:data forKey:key];}-(id)getModelWithClass:(Class)cls forKey:(NSString*)key{NSData*data=[selfgetValueForKey:key];if(!data)returnnil;return[NSKeyedUnarchiver unarchivedObjectOfClass:cls fromData:data error:nil];}@end工具类项目调用示例
// 存储字符串[[NSUserDefaultsTool shareTool]saveValue:@"测试"forKey:@"test_str"];// 安全读取字符串NSString*str=[[NSUserDefaultsTool shareTool]getStringForKey:@"test_str"];// 存储Model[[NSUserDefaultsTool shareTool]saveModel:userConfig forKey:@"user_config"];// 读取ModelUserConfig*config=[[NSUserDefaultsTool shareTool]getModelWithClass:[UserConfig class]forKey:@"user_config"];