NSUserDefaults — 是什么,UserDefaults API 和 iOS 设置操作

作者: IT Sectr 发布日期: 2026-03-12 阅读时间: 10 分钟

NSUserDefaults 是 iOS、watchOS、tvOS 和 macOS 中的键值数据存储,用于保存应用程序的设置和配置。数据存储在应用程序沙盒中的 plist 文件中,并通过 NSUbiquitousKeyValueStore 自动与 iCloud 同步。根据官方文档 Apple Developer, 2025NSUserDefaults 支持存储原始类型:String、Int、Bool、Float、Double、Data、Date、Array 和 Dictionary。从 Swift 3 开始,该类已重命名为 UserDefaults,但其 Objective-C 名称 NSUserDefaults 在代码库和 Apple 文档中仍广泛使用。

要点

  • NSUserDefaults — iOS 和 macOS 上的键值存储,用于在 plist 文件中保存简单的应用程序设置。
  • 支持九种数据类型:String、Int、Bool、Float、Double、Data、Date、Array 和 Dictionary。
  • 数据通过 NSUbiquitousKeyValueStore 自动与 iCloud 同步。
  • 使用 注册(域)系统,沿域链进行级联搜索。
  • 对于敏感数据,Apple 建议使用 Keychain 而非 NSUserDefaults。

什么是 NSUserDefaults?

NSUserDefaults(Swift 中的 UserDefaults)是 Apple 内置的以 plist 格式存储键值对的机制。它适用于所有 Apple 平台:iOS、iPadOS、watchOS、tvOS 和 macOS。主要用途是保存用户偏好、界面状态、首次启动标志、所选选项以及其他在应用程序重启后仍然存在的简单数据。

每个 iOS 应用程序都有一个隔离的沙盒,NSUserDefaults 保存在此沙盒内的 Library/Preferences 目录中,文件名为 Bundle Identifier。plist 文件包含键值对,其中键是字符串,值是支持的类型之一。文件大小没有限制,但 Apple 建议只在 UserDefaults 中存储设置,而不是大量数据。

从 iOS 8 开始,NSUserDefaults 支持 App Groups — 同一开发者应用程序及其扩展(小部件、watchOS 配套应用程序)之间的共享存储。为此使用带有 App Group 标识符的初始化器 init?(suiteName:)。例如,这允许 Today 屏幕上的小部件读取主应用程序的设置,而无需重复保存逻辑。

存储格式:设备上的 plist

物理上,NSUserDefaults 存储在二进制 plist 文件中,路径为:{Sandbox}/Library/Preferences/com.example.myapp.plist。该文件使用二进制 plist 格式(NSPropertyListBinaryFormat_v1_0)以实现紧凑性和读取速度。在 macOS 上,文件可以是 XML 格式以实现兼容性。与 Android 上的 SharedPreferences 不同,UserDefaults 的 plist 文件可以通过 Dictionary 和 Array 包含嵌套结构。

NSUserDefaults 文件默认不加密。数据以明文形式存储,可以通过物理访问设备或通过备份读取。对于存储敏感数据(密码、令牌、加密密钥),Apple 强烈建议使用 Keychain,它在操作系统级别自动加密数据。

NSUserDefaults 在 iOS 中如何工作

NSUserDefaults 基于内存缓存和定期磁盘同步的原理工作。首次访问标准实例 UserDefaults.standard 时,系统将 plist 文件以 Dictionary 形式加载到 RAM 中。所有后续读取都从内存中进行。写入也首先在内存中进行,磁盘同步在后台定期进行。

写入操作使用 set(_:forKey:) 方法,该方法接受可选的 Any? 类型值。值可以为 nil — 用于删除键。以前使用 synchronize() 方法立即写入磁盘,但从 iOS 7 和 OS X 10.9 开始不再需要 — 系统会定期自动同步数据。Apple 在其文档中已正式宣布 synchronize() 是多余的。

UserDefaults 注册和域

NSUserDefaults 使用注册(域)系统来组织值搜索。当应用程序按键请求值时,UserDefaults 按特定顺序依次检查域:首先 NSArgumentDomain(命令行参数),然后是应用程序域(Application),然后是 NSGlobalDomain(系统设置),然后是特定语言域,最后是 NSRegistrationDomain(通过 register(defaults:) 注册的默认值)。

swift
import Foundation

// 标准 UserDefaults 实例
let defaults = UserDefaults.standard

// 写入值
defaults.set("安娜·彼得罗娃", forKey: "username")
defaults.set(28, forKey: "age")
defaults.set(true, forKey: "isLoggedIn")

// 注册默认值
defaults.register(defaults: [
    "theme": "system",
    "fontSize": 14
])

// 读取并返回默认值
let theme = defaults.string(forKey: "theme") ?? "system"
let fontSize = defaults.integer(forKey: "fontSize")

NSRegistrationDomain 域是一个软件域,仅存在于 RAM 中,不会保存到磁盘。它用于设置默认值,这些值在应用程序将自己的值写入应用程序域之前一直有效。这允许创建一个统一的默认设置配置点,可以在开发阶段集中更改。

NSUserDefaults 的主要方法

NSUserDefaults 提供了一组类型化方法来读写数据:string(forKey:)、integer(forKey:)、bool(forKey:)、float(forKey:)、double(forKey:)、data(forKey:)、array(forKey:)、dictionary(forKey:) 和 object(forKey:)。每个读取方法都有相应的写入方法 set(_:forKey:),自动确定存储值的类型。Swift 版本的 UserDefaults 使用严格类型化,但 Objective-C 版本接受并返回 id。

读取方法 (Swift)数据类型默认值
string(forKey:)String?nil
integer(forKey:)Int0
bool(forKey:)Boolfalse
float(forKey:)Float0.0
double(forKey:)Double0.0
data(forKey:)Data?nil

synchronize 及其相关性

NSUserDefaults 中的 synchronize() 方法强制将所有更改从内存写入磁盘。在 iOS 早期版本中,每次写入后都需要调用此方法以保证数据保存。从 iOS 7 开始,系统在后台自动同步 UserDefaults,Apple 已在其文档中正式宣布 synchronize() 是多余的。调用此方法不会导致错误,但不会提供任何额外的保存保证。

为了监视更改,NSUserDefaults 提供了 UserDefaults.didChangeNotification 通知和 addObserver(_:forKeyPath:options:context:) KVO 观察方法。在 SwiftUI 中,可以使用 @AppStorage 属性包装器,它自动将 UserDefaults 中的值与 UI 更新同步。@AppStorage 支持与 UserDefaults 相同的类型,是在 SwiftUI 应用程序中处理设置的首选方式。

swift
// 通过 KVO 观察变化
class SettingsViewModel: NSObject {
    override func observeValue(
        forKeyPath keyPath: String?,
        of object: Any?,
        change: [NSKeyValueChangeKey: Any]?,
        context: UnsafeMutableRawPointer?
    ) {
        guard let keyPath else { return }
        print("键已更改: \(keyPath)")
    }
}

// SwiftUI - AppStorage
struct SettingsView: View {
    @AppStorage("theme") private var theme: String = "system"

    var body: some View {
        Picker("主题", selection: $theme) {
            Text("系统").tag("system")
            Text("浅色").tag("light")
            Text("深色").tag("dark")
        }
    }
}

对于 App Groups(应用程序和扩展之间的共享存储),使用带有 App Group 标识符的初始化器 UserDefaults(suiteName:)。例如,“group.com.example.myapp”。写入此实例的数据可从主应用程序、小部件、watchOS 配套应用程序和属于同一 App Group 的其他扩展中访问。每个套件实例都存储在单独的 plist 文件中。

NSUserDefaults 与存储替代方案

尽管方便简单,但 NSUserDefaults 并不是 iOS 上所有数据类型的通用存储。根据数据量、关键性和安全要求,Apple 提供了多种替代方案,每种方案都针对特定使用场景进行了优化。

解决方案何时使用限制
NSUserDefaults界面设置和配置不适合大数据和秘密
Keychain密码、令牌、加密密钥使用更复杂,速度更慢
CoreData结构化关系数据对于 10-20 个设置来说过于庞大
FileManager文档、图片、二进制数据需要手动文件管理
CloudKit设备间云同步需要 iCloud 帐户和网络连接

Keychain — 安全存储

Keychain 是 Apple 用于机密数据的受保护存储。与 NSUserDefaults 不同,Keychain 中的所有数据都在操作系统级别使用兼容设备上的 Secure Enclave 硬件加密进行加密。Keychain 随设备自动锁定和解锁,并支持通过 Keychain Access Groups 在同一开发者应用程序之间进行访问共享。

Keychain 的主要缺点是 API 的复杂性。要简单地存储一个字符串,需要创建 SecItemAdd 请求,指定属性:类别(kSecClassGenericPassword)、服务(kSecAttrService)、帐户(kSecAttrAccount)和数据本身(kSecValueData)。为了简化 Keychain 的使用,存在第三方包装器,如 KeychainAccess 和 SwiftKeychainWrapper,它们提供了类似于 UserDefaults 的便捷键值接口。

在 Swift 中使用 NSUserDefaults 的示例

让我们看一个实际示例:使用 NSUserDefaults 在 iOS 应用程序中保存和恢复引导状态(欢迎屏幕)。首次启动时,用户看到引导屏幕,完成后标志保存在 UserDefaults 中。后续启动时跳过引导。对于 SwiftUI 使用 @AppStorage,对于 UIKit — 直接访问 UserDefaults.standard。

保存引导状态

让我们创建一个 OnboardingManager 管理器,它封装了与 UserDefaults 的交互以存储引导状态。管理器提供 isOnboardingCompleted 属性用于检查状态,以及 markOnboardingCompleted 方法用于设置标志。存储键已提取到常量中以防止拼写错误。对于单元测试,管理器使用 UserDefaultsProtocol 协议,允许用 MockUserDefaults 替换实际存储。

swift
class OnboardingManager {
    private let defaults: UserDefaults
    private let hasSeenKey = "has_seen_onboarding"

    init(defaults: UserDefaults = .standard) {
        self.defaults = defaults
    }

    var isOnboardingCompleted: Bool {
        defaults.bool(forKey: hasSeenKey)
    }

    func markOnboardingCompleted() {
        defaults.set(true, forKey: hasSeenKey)
    }

    func resetOnboarding() {
        defaults.removeObject(forKey: hasSeenKey)
    }
}

// 在应用中使用
let onboardingManager = OnboardingManager()
if !onboardingManager.isOnboardingCompleted {
    showOnboarding()
} else {
    showMainScreen()
}

对于更复杂的设置,如结构化的 Profile 对象,建议使用 Codable 协议和 JSONEncoder/JSONDecoder。对象通过 JSONEncoder 序列化为 Data,通过 set(_:forKey:) 保存,读取时通过 JSONDecoder 从 Data 反序列化回对象。这种方法允许在 UserDefaults 中存储复杂结构而不损失类型安全。

swift
struct UserProfile: Codable {
    let name: String
    let age: Int
    let preferences: [String: String]
}

extension UserDefaults {
    func save<T: Codable>(_ value: T, forKey key: String) {
        if let data = try? JSONEncoder().encode(value) {
            set(data, forKey: key)
        }
    }

    func load<T: Codable>(_ type: T.Type, forKey key: String) -> T? {
        guard let data = data(forKey: key) else { return nil }
        return try? JSONDecoder().decode(type, from: data)
    }
}

// 使用
let profile = UserProfile(name: "安娜", age: 28, preferences: ["theme": "dark"])
UserDefaults.standard.save(profile, forKey: "user_profile")
let loaded = UserDefaults.standard.load(UserProfile.self, forKey: "user_profile")

重要的是要记住,NSUserDefaults 并非设计用于存储大量数据。Apple 建议将存储数据的大小限制在几十 KB 以内。对于存储大型对象(图像、文档、序列化模型),应使用带有 Documents 目录的 FileManager 或 CoreData。此外,UserDefaults 不支持数据模式版本控制 — 当 Codable 模型结构更改时,旧数据可能无法反序列化,这需要在应用程序代码中处理。

常见问题

NSUserDefaults 与 Android 上的 SharedPreferences 有何不同?

两者都是键值存储,但 NSUserDefaults 支持更多类型(Data、Date、Array、Dictionary)并自动与 iCloud 同步。SharedPreferences 将数据存储在 XML 中,NSUserDefaults 存储在 plist 格式中。NSUserDefaults 具有带级联搜索的域系统,SharedPreferences 使用带有文件名的简单平面结构。

在 NSUserDefaults 中存储密码安全吗?

不安全。NSUserDefaults 以明文形式存储数据而不加密。对于密码、令牌和加密密钥,请使用 Keychain,它在 Secure Enclave 级别加密数据。Keychain 还支持访问属性,如在读取秘密之前进行生物特征认证(Face ID / Touch ID)。

如何在设备之间同步 NSUserDefaults?

要在同一用户的设备之间同步,请使用 NSUbiquitousKeyValueStore — iCloud 云键值存储。在一个设备上写入此服务的数据会自动出现在同一 iCloud 帐户的所有其他设备上。最大容量 — 每个应用程序 1 MB,1024 个键。

如何从 NSUserDefaults 中删除所有数据?

要删除所有数据,请使用应用程序的 Bundle Identifier 调用 removePersistentDomain(forName:) 方法。要删除单个值,请使用 removeObject(forKey:)。要完全重置应用程序设置:UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)。所有删除立即应用于内存缓存。

NSUserDefaults 中数据的最大大小是多少?

Apple 没有对 NSUserDefaults 的大小设置严格限制,但建议所有存储数据的总量不要超过 100 KB。对于大量数据,请使用 CoreData 或 FileManager。当存储超过 1 MB 的数据时,启动应用程序时的读取性能可能会明显下降。

总结

  • NSUserDefaults — Apple 内置的键值存储,以 plist 格式存储简单的应用程序设置。
  • 支持九种数据类型:String、Int、Bool、Float、Double、Data、Date、Array 和 Dictionary。
  • 使用通过 NSRegistrationDomain、NSGlobalDomain 和应用程序域进行 级联搜索 的域系统。
  • 对于设备间同步,Apple 提供 NSUbiquitousKeyValueStore,每个应用程序限制为 1 MB。
  • 为安全起见,对密码和令牌使用 Keychain,而不是 NSUserDefaults。
  • 在 SwiftUI 中,首选的工作方式是带有自动 UI 同步的 @AppStorage 属性包装器。
  • 对于大型或结构化数据,选择 CoreData 或 FileManager 而非 NSUserDefaults。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读