Какие преимущества и недостатки у базы данных Realm по сравнению с Core Data и SQLite?

«Какие преимущества и недостатки у базы данных Realm по сравнению с Core Data и SQLite?» — вопрос из категории Базы данных, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Преимущества:

  • Высокая производительность: Использует собственный движок хранения, оптимизированный для объектов. Часто работает быстрее, чем Core Data с SQLite в качестве бэкенда, особенно на операциях чтения и фильтрации.
  • Реактивные обновления: Подписки (observe) и Results автоматически обновляются при изменении данных, что идеально подходит для связки с UI (например, с UITableView).
  • Прямой доступ к объектам: Не требуется контекст или фетч-запросы для получения объектов. Модели наследуются от Object и сразу готовы к работе.
  • Простая миграция для схемы: Для добавления/удаления полей часто достаточно просто изменить класс модели. Удаление полей и сложные преобразования требуют ручной логики в блоке миграции.
  • Кроссплатформенность: Одна и та же база данных (файл .realm) может использоваться на iOS, Android и других платформах.

Недостатки и ограничения:

  • Размер файла базы данных: Может быстро расти, так Realm резервирует пространство. Требуется периодическое сжатие (Realm.Configuration.shouldCompactOnLaunch или writeCopy).
  • Модель данных наследует от Object: Это накладывает ограничения на дизайн моделей (например, нельзя наследовать от других пользовательских классов).
  • Потоковая безопасность: Объекты Realm привязаны к потоку, на котором были созданы. Для передачи между потоками необходимо использовать ThreadSafeReference или заново запрашивать объект по первичному ключу в другом потоке.
  • Отсутствие каскадного удаления "из коробки": При удалении родительского объекта связанные дочерние объекты не удаляются автоматически, это нужно делать вручную.
  • Проприетарная лицензия: Для некоторых сценариев использования требуется коммерческая лицензия.

Примеры использования и миграции:

// 1. Определение модели
import RealmSwift

class Dog: Object {
    @Persisted(primaryKey: true) var id: ObjectId
    @Persisted var name: String
    @Persisted var age: Int
    @Persisted var owner: Person? // To-one связь
    @Persisted var toys: List<Toy> // To-many связь
}

// 2. Конфигурация с шифрованием и миграцией
let config = Realm.Configuration(
    schemaVersion: 2,
    encryptionKey: getEncryptionKey(),
    migrationBlock: { migration, oldSchemaVersion in
        if oldSchemaVersion < 2 {
            // Миграция с версии 1 на 2: добавляем новое поле 'breed'
            migration.enumerateObjects(ofType: Dog.className()) { oldObject, newObject in
                newObject!["breed"] = "Unknown"
            }
        }
    }
)

let realm = try! Realm(configuration: config)

// 3. Реактивные наблюдения
let puppies = realm.objects(Dog.self).filter("age < 2")
let token = puppies.observe { changes in
    switch changes {
    case .initial(let results):
        print("Initial load: (results.count) puppies")
    case .update(let results, let deletions, let insertions, let modifications):
        // Автоматически обновить UITableView/UICollectionView
        print("Changes: +\(insertions.count), -\(deletions.count), ~\(modifications.count)")
    case .error(let error):
        print("Error: (error)")
    }
}

// 4. Запись в транзакции
try! realm.write {
    let newDog = Dog()
    newDog.name = "Rex"
    newDog.age = 1
    realm.add(newDog)
}

// Не забудьте инвалидировать токен наблюдения
// token.invalidate()