Почему не рекомендуется хранить большие данные в UserDefaults?

Ответ

UserDefaults (по умолчанию, UserDefaults.standard) оптимизирован для хранения небольших объемов данных (настроек, флагов, простых значений). Использование его для больших данных ведет к проблемам:

Основные причины:

  1. Производительность при запуске: Весь plist-файл UserDefaults загружается в память при старте приложения. Большой объем данных увеличивает время запуска и потребление оперативной памяти.
  2. Отсутствие атомарности и транзакций: Запись большого значения (например, Data) — это операция write(to:) всего plist-файла. Это может быть медленно и создает риск повреждения данных при прерывании.
  3. Ненадежность для критичных данных: Система может в крайних случаях очистить файлы UserDefaults вашего приложения для освобождения места.
  4. Неофициальные лимиты: На практике проблемы начинаются после ~500 КБ. Чтение/запись становятся заметно медленными.
Практические альтернативы: Тип данных Рекомендуемое хранилище
Настройки, флаги, строки UserDefaults (идеально)
Структурированные данные (списки, объекты) Файловая система (JSON/Property List в Documents/) + Codable
Сложные запросы, отношения База данных (SQLite, Core Data, Realm)
Чувствительные данные (пароли, токены) Keychain Services

Пример кода:

// ❌ Плохо для больших данных
let largeImageData = UIImage.pngData()
UserDefaults.standard.set(largeImageData, forKey: "avatar")

// ✅ Правильно: файловая система
func saveData(_ data: Data) throws {
    let fileURL = FileManager.default
        .urls(for: .documentDirectory, in: .userDomainMask)[0]
        .appendingPathComponent("avatar.png")
    try data.write(to: fileURL)
    // В UserDefaults сохраняем только путь
    UserDefaults.standard.set(fileURL.path, forKey: "avatarPath")
}

Ответ 18+ 🔞

А, ну это же классика, блядь! Смотри, народ часто пытается впихнуть в UserDefaults всё, что не приколочено, а потом охуевает, почему приложение грузится как черепаха в сиропе.

Вот смотри, UserDefaults.standard — это не волшебный мешок, куда можно совать всё подряд, ёпта. Он заточен под мелкую фигню: настройки, флажки, какие-то простые значения. А как только начинаешь пихать туда что-то объёмное — начинается пиздец, Карл!

Почему так, нахуй?

  1. Запуск превращается в ад. При старте приложения весь этот plist-файл UserDefaults загружается в память целиком. Представь, ты туда гигабайт фоток засунул. Приложение открывается, а оно вместо того чтобы работать, полчаса этот файл в оперативку тащит. Пиздец, а не юзер-экспириенс.

  2. Запись — это пиздец какой дорогой. Когда ты пишешь туда даже один большой кусок данных (типа Data с картинкой), система на самом деле перезаписывает весь файл целиком, блядь. Это как если бы ты, чтобы поменять одну лампочку, перестраивал весь дом. Медленно, ресурсоёмко, и если в процессе что-то пойдёт не так — файл может накрыться медным тазом.

  3. Система может его просто взять и выкинуть. Да-да, в крайних случаях, когда место на устройстве кончается, система может почистить файлы UserDefaults твоего приложения, как ненужный хлам. А ты потом будешь искать, куда делся твой овердохуищный JSON с данными. Доверия к нему — ноль ебать.

  4. Лимиты из воздуха. Официально их нет, но на практике, как только переваливаешь за ~500 КБ, всё начинает тормозить так, что волосы дыбом встают. Чтение, запись — всё ебётся.

Так куда же это всё девать, ёпта?

Смотри табличку, не тупи:

Что храним? Куда пихать?
Настройки, флаги, строки (типа "темная тема включена") UserDefaults (идеально, для этого и создан)
Структурированная хуйня (списки, объекты, JSON) Файловая система (кидай в Documents/ как JSON или Property List) + Codable
Сложные данные с запросами и связями База данных (SQLite, Core Data, Realm — выбирай по вкусу)
Пароли, токены и прочую секретную лабуду Keychain Services (сюда, только сюда!)

Пример, чтобы дошло наконец:

// ❌ Пиздец как плохо. Не делай так никогда.
let hugeFuckingImageData = UIImage.pngData()
UserDefaults.standard.set(hugeFuckingImageData, forKey: "userAvatar") // Ёбта, что ты делаешь?!

// ✅ Правильно, как взрослый дядя. Сохраняем в файл.
func saveData(_ data: Data) throws {
    // Находим папку Documents
    let fileURL = FileManager.default
        .urls(for: .documentDirectory, in: .userDomainMask)[0]
        .appendingPathComponent("avatar.png") // Имя файла
    try data.write(to: fileURL) // Пишем данные в файл
    // А в UserDefaults кладём ТОЛЬКО ПУТЬ к файлу — это быстро и безопасно
    UserDefaults.standard.set(fileURL.path, forKey: "avatarPath")
}

Вот и вся магия, блядь. Не пытайся засунуть диван в холодильник — используй инструменты по назначению.