本篇目标
理解 UserDefaults 的用途与限制,掌握基本类型的存储、读取、删除,学会封装 UserDefaults 管理类,能正确处理默认值与数据类型。
一、UserDefaults 是什么
UserDefaults 是 iOS 提供的轻量级键值存储,用于保存用户偏好和少量数据。
适合存储:
用户设置(主题、字号、语言)
开关状态(是否开启通知)
简单标记(是否首次启动、是否已登录)
少量用户信息(用户名、昵称)
不适合存储:
大量数据(图片、文件、列表)
敏感信息(密码、Token)
复杂对象(应使用文件或数据库)
特点:
基于 plist 文件存储。
数据自动持久化。
读写速度快。
不需要手动保存。
应用重启后数据仍在。
二、基础读写
存储:
UserDefaults.standard.set("Tom", forKey: "username")
UserDefaults.standard.set(25, forKey: "age")
UserDefaults.standard.set(true, forKey: "isFirstLaunch")
UserDefaults.standard.set(1.75, forKey: "height")读取:
let username = UserDefaults.standard.string(forKey: "username")
let age = UserDefaults.standard.integer(forKey: "age")
let isFirstLaunch = UserDefaults.standard.bool(forKey: "isFirstLaunch")
let height = UserDefaults.standard.double(forKey: "height")对应读取方法:
存储类型 | 读取方法 | 默认值 |
|---|---|---|
String |
| nil |
Int |
| 0 |
Bool |
| false |
Double |
| 0.0 |
Float |
| 0.0 |
Data |
| nil |
Array |
| nil |
Dictionary |
| nil |
注意:
string(forKey:)返回可选类型。integer(forKey:)等返回非可选类型,不存在时返回默认值。类型不匹配时读取可能失败。
三、删除与检查
删除单个键:
UserDefaults.standard.removeObject(forKey: "username")检查键是否存在:
if UserDefaults.standard.object(forKey: "username") != nil {
print("存在")
}注意:
不能通过
string(forKey:) == nil判断,因为存储空字符串也会返回空字符串。用
object(forKey:)判断更准确。
删除所有数据:
if let domain = Bundle.main.bundleIdentifier {
UserDefaults.standard.removePersistentDomain(forName: domain)
}慎用:会清除应用所有 UserDefaults 数据。
四、默认值处理
使用 `??` 提供默认值:
let username = UserDefaults.standard.string(forKey: "username") ?? "Guest"注册默认值:
UserDefaults.standard.register(defaults: [
"username": "Guest",
"fontSize": 14,
"isDarkMode": false
])
let username = UserDefaults.standard.string(forKey: "username") // Guest说明:
register注册的默认值不写入磁盘。只在键不存在时生效。
适合应用启动时初始化默认配置。
五、存储数组与字典
存储数组:
let fruits = ["apple", "banana", "orange"]
UserDefaults.standard.set(fruits, forKey: "fruits")
let saved = UserDefaults.standard.stringArray(forKey: "fruits")存储字典:
let settings = ["theme": "dark", "fontSize": 14]
UserDefaults.standard.set(settings, forKey: "settings")
let savedSettings = UserDefaults.standard.dictionary(forKey: "settings")注意:
数组和字典元素必须是 plist 支持的类型。
支持类型:String、Int、Double、Bool、Date、Data、Array、Dictionary。
六、存储自定义对象
UserDefaults 不能直接存储自定义对象,需要先编码。
使用 Codable + JSON:
struct User: Codable {
var name: String
var age: Int
var isVIP: Bool
}
// 存储
let user = User(name: "Tom", age: 25, isVIP: true)
if let data = try? JSONEncoder().encode(user) {
UserDefaults.standard.set(data, forKey: "currentUser")
}
// 读取
if let data = UserDefaults.standard.data(forKey: "currentUser"),
let savedUser = try? JSONDecoder().decode(User.self, from: data) {
print(savedUser.name)
}说明:
Codable让对象可以编码和解码。存储的是 Data 类型。
读取时解码回对象。
七、封装 UserDefaults 管理类
直接使用 UserDefaults 键名容易出错,建议封装。
定义 Keys:
enum UserDefaultsKeys {
static let username = "username"
static let fontSize = "fontSize"
static let isDarkMode = "isDarkMode"
static let isFirstLaunch = "isFirstLaunch"
}封装管理类:
class AppSettings {
static let shared = AppSettings()
private let defaults = UserDefaults.standard
private init() {
defaults.register(defaults: [
UserDefaultsKeys.username: "Guest",
UserDefaultsKeys.fontSize: 14,
UserDefaultsKeys.isDarkMode: false,
UserDefaultsKeys.isFirstLaunch: true
])
}
var username: String {
get { defaults.string(forKey: UserDefaultsKeys.username) ?? "Guest" }
set { defaults.set(newValue, forKey: UserDefaultsKeys.username) }
}
var fontSize: Int {
get { defaults.integer(forKey: UserDefaultsKeys.fontSize) }
set { defaults.set(newValue, forKey: UserDefaultsKeys.fontSize) }
}
var isDarkMode: Bool {
get { defaults.bool(forKey: UserDefaultsKeys.isDarkMode) }
set { defaults.set(newValue, forKey: UserDefaultsKeys.isDarkMode) }
}
var isFirstLaunch: Bool {
get { defaults.bool(forKey: UserDefaultsKeys.isFirstLaunch) }
set { defaults.set(newValue, forKey: UserDefaultsKeys.isFirstLaunch) }
}
}使用:
AppSettings.shared.username = "Tom"
print(AppSettings.shared.username) // Tom优点:
键名集中管理。
类型安全。
默认值统一注册。
便于维护和修改。
八、属性包装器封装
可以用 @propertyWrapper 简化。
@propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
var wrappedValue: T {
get {
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
enum Settings {
@UserDefault(key: "username", defaultValue: "Guest")
static var username: String
@UserDefault(key: "fontSize", defaultValue: 14)
static var fontSize: Int
@UserDefault(key: "isDarkMode", defaultValue: false)
static var isDarkMode: Bool
}
Settings.username = "Tom"
print(Settings.username) // Tom优点:
语法简洁。
类型安全。
可读性高。
注意:
属性包装器需要泛型支持。
存储复杂类型时注意可选性。
九、与 SwiftUI 结合
@AppStorage:SwiftUI 提供的属性包装器,底层使用 UserDefaults。
struct SettingsView: View {
@AppStorage("username") private var username = "Guest"
@AppStorage("isDarkMode") private var isDarkMode = false
@AppStorage("fontSize") private var fontSize = 14
var body: some View {
Form {
TextField("用户名", text: $username)
Toggle("深色模式", isOn: $isDarkMode)
Stepper("字号:\(fontSize)", value: $fontSize, in: 12...24)
}
}
}特点:
自动读写 UserDefaults。
值变化时自动刷新界面。
支持 String、Int、Double、Bool、URL、Data。
指定存储:
@AppStorage("username", store: UserDefaults(suiteName: "group.com.example"))
private var username = "Guest"用于 App Group 共享:适合主 App 与扩展之间共享数据。
十、观察变化
监听单个键:
NotificationCenter.default.addObserver(
forName: UserDefaults.didChangeNotification,
object: nil,
queue: .main
) { _ in
print("UserDefaults 发生变化")
}注意:
didChangeNotification是整个 UserDefaults 变化。不能精确到具体键。
真实项目中,通常用 @AppStorage 或 KVO。
十一、使用注意事项
注意事项:
不要存敏感信息。
不要存大量数据。
键名要有命名规范。
数据大小控制在几百 KB 以内。
应用卸载后 UserDefaults 数据会被清除。
十二、避坑指南
敏感信息不要存:密码、Token 应使用 Keychain。
不要存大量数据:大数组、图片用文件或数据库。
不要用 string(forKey:) 判断存在:用 object(forKey:)。
键名要统一管理:避免硬编码字符串。
默认值要注册:否则第一次读取可能为空。
类型要一致:存 String 读 String,不要混用。
@AppStorage 只支持基本类型:自定义对象需要编码。
十三、检查清单
[ ] 理解 UserDefaults 的用途与限制
[ ] 会存储和读取基本类型
[ ] 会删除和检查键
[ ] 会处理默认值
[ ] 能存储数组和字典
[ ] 能存储自定义对象(Codable)
[ ] 会封装 UserDefaults 管理类
[ ] 会使用 @AppStorage
[ ] 了解 UserDefaults 的性能边界