iOS OC NSUserDefaults

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:用户登录状态保存 & 退出登录清空)
    • 六、项目避坑
    • 七、完整工具类

一、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"];
相关推荐
2601_955767421 小时前
移动OLED屏幕偏振光缺失的补偿方案:圆偏振光还原与磁控溅射AR协同光学系统设计
ios·ar·iphone·圆偏振光护眼·iphone17护眼钢化膜·#观复盾护景贴
sweet丶1 小时前
Swift async/await并发框架深入总结
ios
元Y亨H9 小时前
Mac 进阶效率手册:从操作熟练到效率飞升
macos
小雨下雨的雨1 天前
iOS风格计算器 - 鸿蒙PC Electron框架上的技术实现详解
游戏·ios·华为·electron·harmonyos·鸿蒙
开开心心就好1 天前
无弹窗不更新的PC本地播放工具
运维·科技·macos·docker·计算机外设·ocr·powerpoint
小王师傅661 天前
深入解析:Docker在Mac上的运行本质与Linux进程管理机制
linux·macos·docker
微步_ym1 天前
Docker:在Mac的docker.desktop中安装mongo
macos·docker·容器
fukai77221 天前
Parallels Desktop 26 :Mac 上运行 Windows 的最佳虚拟化方案
macos