1. Введение в Realm
Realm представляет собой мобильную базу данных: замена для SQLite
& Core Data
iOS:
Приложения, использующие Realm могут работать: iOS7 или выше, OS
X 10.9 или выше, WatchKit.
Xcode 6.4 или выше.
Objective-C, Swift 1.2 & Swift 2.x поддерживаются.
Android:
JDK версии> = 7.
Все Android версии начиная с уровня API 9 (Android 2.3 Gingerbread и
выше) поддерживаются.
2. Модели (code-first approach)
@class Person;
// Dog model
@interface Dog : RLMObject
@property NSString *name;
@property Person *owner;
@end
RLM_ARRAY_TYPE(Dog) // define RLMArray<Dog>
// Person model
@interface Person : RLMObject
@property NSString *name;
@property NSDate *birthdate;
@property RLMArray<Dog *><Dog> *dogs;
@end
RLM_ARRAY_TYPE(Person) // define RLMArray<Person>
Модели данных Realm определяются с помощью классов Objective-C со свойствами.
Унаследуйте RLMObject или существующий класс модели для создания объектов модели
данных Realm. Объекты Realm в основном функционируют как и любые другие объекты
Objective-C - вы можете добавить свои собственные методы и протоколы к ним и
использовать их, как и любой другой объект. Основные ограничения в том, что вы можете
использовать объект только в потоке, в котором он был создан, и вы не можете получить
доступ к его ivar для обращения к его свойствам
3. Типы данных
Realm поддерживает следующие типы данных: BOOL, INT, NSInteger, float, double,
NSString, NSDate (обрезается до секунд), NSData и NSNumber помеченый
определенным типом, напр. NSNumber<RLMInt>.
Вы можете использовать RLMArray <Object *> <Object> и RLMObject чтобы
моделировать такие связи (relations), как to-many и to-one.
RLMArrays поддерживают дженерики Objective-C во время компиляции. Вот что
означают различные компоненты определения свойства, и почему они полезны:
RLMArray: Тип свойства.
<Object *>: Специализация дженерика. Это помогает предотвратить
использование массива с неправильным типом объекта во время компиляции.
<Объект>: Протокол, который поддерживает этот RLMArray. Это позволяет Realm
знать, как специализировать схемы этой модели во время выполнения.
4. Отношения (relationships)
To-One отношения
Для many-to-one or one-to-one отношений, просто определите свойство типа
наследника RLMObject
// Dog.h
@interface Dog : RLMObject
// ... other property declarations
@property Person *owner;
@end
5. To-Many отношения
Вы можете определить отношение to-many, используя свойства RLMArray.
RLMArray содержат другие объекты одного типа и имеют интерфейс, очень
похожий на NSMutableArray.
Чтобы добавить свойство dogs в нашей модели Person, которая позволяет
установить отношение с несколькими объектами Dog, мы должны сначала
определить тип RLMArray <Dog>. Это делается с помощью макроса в нижней
части соответствующей модели интерфейса
@interface Person : RLMObject
@property NSString *name;
@property NSDate *birthdate;
@property RLMArray<Dog *><Dog> *dogs;
@end
RLM_ARRAY_TYPE(Dog) // define RLMArray<Person>
6. Миграция приложений с Core Data
на Realm
1. Удалить Core Data Framework
2. Удалить код инициализации Core Data
В Core Data, изменения объектов производятся в объектах managed object
context. Объекты managed object context создаются в рамках persistent store
coordinator, который, в свою очередь, связан с managed object model
RLMRealm *defaultRealm = [RLMRealm defaultRealm];
3. Мигрировать файлы моделей
Унаследовать от RLMObject вместо NSManagedObject
7. @implementation Dog
+ (NSString *)primaryKey
{
return @"uuid";
}
+ (NSDictionary *)defaultPropertyValues
{
return @{ @"uuid" : [[NSUUID UUID] UUIDString],
@"name" : @"",
@"birthdate" : [NSDate date]};
}
@end
В то время как объекты основных данных, имеют внутреннее свойство
NSManagedObjectID чтобы однозначно идентифицировать объекты, Realm
оставляет это решение на Вас, как разработчика. В приведенном выше примере,
добавляется дополнительное свойство с именем UUID, а затем используется
метод [RLMObject PrimaryKey], чтобы пометить его как уникальный
идентификатор для этого класса. Тем не менее, если ваши объекты вообще не
должны быть однозначно определен, все это может быть пропущено.
8. 4. Миграция операций записи
CoreData:
//Create a new Dog
Dog *newDog = [NSEntityDescription insertNewObjectForEntityForName:@"Dog"
inManagedObjectContext:myContext];
newDog.name = @"McGruff";
//Save the new Dog object to disk
NSError *saveError = nil;
[newDog.managedObjectContext save:&saveError];
//Rename the Dog
newDog.name = @"Pluto";
[newDog.managedObjectContext save:&saveError];
9. Realm:
//Create the dog object
Dog *newDog = [[Dog alloc] init];
newDog.name = @"McGruff";
//Save the new Dog object to disk (Using a block for the transaction)
RLMRealm *defaultRealm = [RLMRealm defaultRealm];
[defaultRealm transactionWithBlock:^{
[defaultRealm addObject:newDog];
}];
//Rename the dog (Using open/close methods for the transaction)
[defaultRealm beginWriteTransaction];
newDog.name = @"Pluto";
[defaultRealm commitWriteTransaction];
10. 5. Миграция запросов
В базовой реализации, Core Data использует концепцию запросов для извлечения
данных с диска (fetch request). Объект запроса выборки создается, а затем
дополнительные параметры фильтрации и сортировки создаются и добавляются к
объекту запроса
NSManagedObjectContext *context = self.managedObjectContext;
//A fetch request to get all dogs younger than 5 years old, in alphabetical order
NSEntityDescription *entity = [NSEntityDescription
entityForName:@"Dog" inManagedObjectContext:context];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age < 5"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = entity;
request.predicate = predicate;
request.sortDescriptors = @[sortDescriptor];
NSError *error;
NSArray *dogs = [moc executeFetchRequest:request error:&error];
11. Сравните, как это все делается в Realm:
RLMResults *dogs = [[Dog objectsWhere:@"age < 5"]
sortedResultsUsingProperty:@"name" ascending:YES];
12. 6. Concurrency
Иногда вам может понадобиться передать объект Realm между потоками. В то время как Core Data
позволяет передавать объекты (managed object) между потоками (хотя это не лучшая практика), в случае
Realm, передача объектов между потоками явно запрещена, и любые попытки сделать это приведут к
исключению (exception).
Самый простой способ обойти это ограничение, использовать GCD чтобы запустить транзакцию
Realm в одном потоке, а получить результат в главном потоке (main thread).
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//Rename the dog in a background queue
[[RLMRealm defaultRealm] transactionWithBlock:^{
dog.name = @"Peanut Wigglebutt";
}];
//Print the dog's name on the main queue
NSString *uuid = dog.uuid;
dispatch_async(dispatch_get_main_queue(), ^{
Dog *localDog = [Dog objectForPrimaryKey:uuid];
NSLog(@"Dog's name is %@", localDog.name);
});
});