Ответ
Преимущества:
- Высокая производительность: Использует собственный движок хранения, оптимизированный для объектов. Часто работает быстрее, чем 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() Ответ 18+ 🔞
Ага, слушай, вот этот ваш Realm, ну, такая штука, блядь. Как будто взяли Core Data, да по ебалу ему настучали, чтобы не выёбывался со своими контекстами и фетчами. В общем, расскажу по-простому, что тут к чему.
Что в нём, блядь, хорошего, а?
- Быстрый, как хуй с горы. У них там свой движок, под объекты заточенный. Читает и фильтрует так, что Core Data с её SQLite-бэкендом иногда курит в сторонке, особенно когда данные просто взять надо.
- Сам всё обновляет, хитрая жопа. Подписки (
observe) и результаты (Results) — они живые. Изменились данные в базе — тут же в интерфейсе всё дернулось. ДляUITableViewэто вообще ёперный театр, красота. - Объекты прямо в руки. Не надо через хуй с контекстами прыгать. Наследовал модель от
Object— и всё, работай с ней как есть. Никаких фетч-реквестов, ебать мои старые костыли. - Миграции часто простые. Добавил поле в класс — и в рот меня чих-пых, в базе оно появилось. Правда, если поле удаляешь или сложное преобразование нужно — тут уже руками в блоке миграции повозиться придётся.
- Кроссплатформенный. Файлик
.realmи на iOS, и на Android пойдёт. Один и тот же, блядь.
А теперь, сука, подводные камни:
- Файл базы жиреет, как на дрожжах. Realm любит место про запас резервировать. Так что периодически его надо сжимать, иначе овердохуища гигабайтов наберет. Есть
shouldCompactOnLaunchилиwriteCopyдля этого. - Модели — рабы
Object. От этого класса наследоваться надо, а от своих собственных — низя. Дизайн иногда, блядь, кривоватый получается. - Потоковая безопасность — отдельная песня. Объект, созданный в одном потоке, в другом — просто кусок говна, непригодный. Тащить между потоками надо через
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? // Связь один-к-одному
@Persisted var toys: List<Toy> // Связь один-ко-многим
}
// 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("Загрузили сразу: (results.count) щенков")
case .update(let results, let deletions, let insertions, let modifications):
// Идеально для апдейта таблицы. Сам всё посчитал, сука.
print("Изменения: +(insertions.count), -(deletions.count), ~(modifications.count)")
case .error(let error):
print("Ошибка, блядь: (error)")
}
}
// 4. Запись. ТОЛЬКО в транзакции! Иначе — исключение, иди нахуй.
try! realm.write {
let newDog = Dog()
newDog.name = "Rex"
newDog.age = 1
realm.add(newDog)
}
// И НЕ ЗАБЫВАЙ токен наблюдения убить, а то память потечёт!
// token.invalidate()
Вот, в общем-то, и всё. Инструмент мощный, но со своими тараканами. Если в лоб по учебнику использовать — норм. Начинаешь выёбываться со сложными связями между потоками — готовься, блядь, волосы на жопе рвать.