Objective?C版本的 Realm 能夠讓您以一種安全悄雅、耐用以及迅捷的方式來高效地編寫應(yīng)用的數(shù)據(jù)模型層
數(shù)據(jù)模型(Model)
Realm數(shù)據(jù)模型是基于標(biāo)準(zhǔn) Objective?C 類來進(jìn)行定義的,使用屬性來完成模型的具體定義。通過簡單的繼承 RLMObject
或者一個(gè)已經(jīng)存在的模型類,您就可以創(chuàng)建一個(gè)新的 Realm 數(shù)據(jù)模型對象纽什。
Realm模型對象在形式上基本上與其他 Objective?C 對象相同 - 您可以給它們添加您自己的方法(method)和協(xié)議(protocol)枫慷,和在其他對象中使用類似探孝。
您只需要為對象的類型列表添加目標(biāo)類型的屬性足丢,或者 RLMArray
栖疑,就可以創(chuàng)建數(shù)據(jù)關(guān)系(relationship)和嵌套數(shù)據(jù)結(jié)構(gòu)(nested data structure)。
#import <Realm/Realm.h>
@class Person;
// 狗狗的數(shù)據(jù)模型
@interface Dog : RLMObject
@property NSString *name;
@property Person *owner;
@end
RLM_ARRAY_TYPE(Dog) // 定義RLMArray<Dog>
// 狗狗主人的數(shù)據(jù)模型
@interface Person : RLMObject
@property NSString *name;
@property NSDate *birthdate;
@property RLMArray<Dog> *dogs;
@end
RLM_ARRAY_TYPE(Person) // 定義RLMArray<Person>
支持的類型
Realm支持以下的屬性類型:BOOL
int
NSInteger
long
float
double
NSString
NSDate
NSData
以及 被特殊類型標(biāo)記的 NSNumber 。
您可以使用 RLMArray<Object *><Object>
和 RLMObject
的子類來建立諸如一對多、一對一之類的關(guān)系模型。
在 Xcode 7 以及之后的版本中蜀细,RLMArray支持編譯時(shí)的 Objective?C 泛型(generics)塘娶。下面是不同屬性定義方法的意義以及用途:
// RLMArray: 屬性類型。
// <Object *>: 屬性的特別化(generic specialization)鸦难,這可以阻止在編譯時(shí)使用錯(cuò)誤對象類型的數(shù)組。
// <Object>: 此RLMArray遵守的協(xié)議,可以讓 Realm 知曉如何在運(yùn)行時(shí)確定數(shù)據(jù)模型的架構(gòu)。
關(guān)系(Relationships)
RLMObject
能夠借助 RLMObject
以及 RLMArray
屬性來和另一個(gè) RLMObject
建立聯(lián)系。 RLMArray
的接口和 NSArray
非常類似等太,在 RLMArray
中的對象能夠通過索引下標(biāo)(indexed subscripting)進(jìn)行訪問包颁。 與 NSArray
所不同的是,RLMArray
的類型是固定的,其中只能存放簡單的 RLMObject
子類類型。 要了解更詳細(xì)的信息,請參閱 RLMArray萄涯。
假設(shè)現(xiàn)在您已經(jīng)定義好了 Person 數(shù)據(jù)模型(見上文),讓我們創(chuàng)建另一個(gè)名為 Dog 的數(shù)據(jù)模型:
// Dog.h
@interface Dog : RLMObject
@property NSString *name;
@end
對一(To-One)關(guān)系
對于多對一(many-to-one)或者一對一(one-to-one)關(guān)系來說伯襟,只需要聲明一個(gè) RLMObject
子類類型的屬性即可:
// Dog.h
@interface Dog : RLMObject
// 其余屬性聲明...
@property Person *owner;
@end
您可以非常簡單的通過這個(gè)屬性完成關(guān)系的綁定:
Person *jim = [[Person alloc] init];
Dog *rex = [[Dog alloc] init];
rex.owner = jim;
當(dāng)使用 RLMObject
屬性的時(shí)候,您可以通過正常的屬性訪問語法來訪問嵌套屬性。比如說,rex.owner?.address.country
會依次讀取對象的屬性沮稚,然后自動從 Relam 中匹配所需的每一個(gè)對象盛杰。
對多(To-Many)關(guān)系
通過 RLMArray
類型的屬性您可以定義一個(gè)對多關(guān)系逗嫡。RLMArray
中可以包含簡單類型的 RLMObject
雷滚,其接口與 NSMutableArray
非常類似车份。
RLMArray
可能會包含多個(gè)相同 Realm 對象的引用严就,即便對象帶有主鍵也是如此铸董。例如,您或許會創(chuàng)建一個(gè)空的 RLMArray
沉衣,然后連續(xù)三次向其中插入同一個(gè)對象栋艳;當(dāng)使用 0、1吸占、2 的索引來訪問元素的時(shí)候晴叨,RLMArray
將會返回對應(yīng)的對象,而所返回的這三個(gè)對象都是同一個(gè)對象矾屯。
如果要給我們的 Person
數(shù)據(jù)模型添加一個(gè) “dogs”
屬性兼蕊,以便能夠和多個(gè) “dogs”
建立關(guān)系,也就是表明一個(gè)「人」可以養(yǎng)多條「狗」件蚕,那么我們首先需要定義一個(gè) RLMArray<Dog>
類型孙技。通過對應(yīng)數(shù)據(jù)模型接口文件下的宏命令即可完成:
//Dog.h
@interface Dog : RLMObject
// 屬性聲明...
@end
**
RLM_ARRAY_TYPE(Dog) // 定義一個(gè) RLMArray<Dog> 類型
RLM_ARRAY_TYPE 宏創(chuàng)建了一個(gè)協(xié)議产禾,從而允許 RLMArray<Dog> 語法的使用。如果該宏沒有放置在模型接口的底部的話牵啦,您或許需要提前聲明該模型類亚情。
**
接下來您就能定義RLMArray<Dog>類型的屬性了:
```objc
// Person.h
@interface Person : RLMObject
// 其余的屬性聲明...
@property RLMArray<Dog *><Dog> *dogs;
@end
您可以和之前一樣,對 RLMArray 屬性進(jìn)行訪問和賦值:
// jim 是 rex 以及所有名字叫“Fido”的狗狗的主人
RLMResults<Dog *> *someDogs = [Dog objectsWhere:@"name contains 'Fido'"];
[jim.dogs addObjects:someDogs];
[jim.dogs addObject:rex];
注意:雖然可以給 RLMArray 屬性賦值為 nil哈雏,但是這僅用于“清空”數(shù)組势似,而不是用以移除數(shù)組。這意味著您總是可以向一個(gè) RLMArray 屬性中添加對象僧著,即使其被置為了 nil履因。
RLMArray 屬性將確保其當(dāng)中的插入次序不會被擾亂。
這里需要強(qiáng)調(diào)的是嵌套數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)關(guān)系的使用盹愚,即對 RLMArray 的使用栅迄。RLMArray 是Realm的數(shù)組,只能存放對象類型的數(shù)據(jù)皆怕。在使用 RLMArray 時(shí)需要注意:RLM_ARRAY_TYPE 宏創(chuàng)建了一個(gè)協(xié)議毅舆,從而允許 RLMArray<Dog> 語法的使用。如果該宏沒有放置在模型接口的底部的話愈腾,您或許需要提前聲明該模型類憋活。
實(shí)用例
#import <Realm/Realm.h>
@interface BKUserPayWay : RLMObject
@property NSString *userPayWayName;
@property NSString *userPayWayValue;
@end
RLM_ARRAY_TYPE(BKUserPayWay) //定義RLMArray< BKUserPayWay >
@interface BKUserStatusInfo : RLMObject
@property NSString * userNum;
@property NSString * userPwd;
@property NSString * deveiceToken;
//有糖小店支付 信息
@property NSString * userSecretKey;
@property NSString * userValidTime;
@property RLMArray <BKUserPayWay *><BKUserPayWay>* userPayWays;
反向關(guān)系(Inverse Relationship)
鏈接是單向性的。因此虱黄,如果對多關(guān)系屬性 Person.dogs
鏈接了一個(gè) Dog
實(shí)例悦即,而這個(gè)實(shí)例的對一關(guān)系屬性 Dog.owner
又鏈接到了對應(yīng)的這個(gè) Person
實(shí)例,那么實(shí)際上這些鏈接仍然是互相獨(dú)立的橱乱。為 Person
實(shí)例的 dogs
屬性添加一個(gè)新的 Dog
實(shí)例辜梳,并不會將這個(gè) Dog
實(shí)例的 owner
屬性自動設(shè)置為該 Person
。但是由于手動同步雙向關(guān)系會很容易出錯(cuò)泳叠,并且這個(gè)操作還非常得復(fù)雜作瞄、冗余,因此 Realm 提供了 “鏈接對象 (linking objects)”
屬性來表示這些反向關(guān)系危纫。
借助鏈接對象屬性宗挥,您可以通過指定的屬性來獲取所有鏈接到指定對象的對象。例如种蝶,一個(gè) Dog
對象可以擁有一個(gè)名為 owners
的鏈接對象屬性契耿,這個(gè)屬性中包含了某些 Person
對象,而這些 Person
對象在其 dogs
屬性中包含了這一個(gè)確定的 Dog
對象蛤吓。您可以將 owners
屬性設(shè)置為 RLMLinkingObjects
類型宵喂,然后重寫 +[RLMObject linkingObjectsProperties]
來指明關(guān)系糠赦,說明 ownders
中包含了 Person
模型對象会傲。
@interface Dog : RLMObject
@property NSString *name;
@property NSInteger age;
@property (readonly) RLMLinkingObjects *owners;
@end
@implementation Dog
+ (NSDictionary *)linkingObjectsProperties {
return @{
@"owners": [RLMPropertyDescriptor descriptorWithClass:Person.class propertyName:@"dogs"],
};
}
@end
可空屬性(Optional Properties)
通常情況下锅棕,NSString *
、NSData *
以及 NSDate *
屬性可以設(shè)置為 nil淌山。如果你不需要實(shí)現(xiàn)此功能裸燎,你可以重寫您的 RLMObject
子類的 +requiredProperties
方法。
比如對于以下的模型定義來說泼疑,如果嘗試給 name
屬性設(shè)置為 nil
將會拋出一個(gè)異常,但是將 birthday
屬性設(shè)置為 nil
卻是允許的:
@interface Person : RLMObject
@property NSString *name;
@property NSDate *birthday;
@end
@implementation Person
+ (NSArray *)requiredProperties {
return @[@"name"];
}
@end
存儲可空數(shù)字目前已經(jīng)可以通過 NSNumber *
屬性完成。
由于 Realm 對不同類型的數(shù)字采取了不同的存儲格式缓熟,因此設(shè)置可空的數(shù)字屬性必須是 RLMInt
犀勒、RLMFloat
、RLMDouble
或者 RLMBool
類型会油。所有賦給屬性的值都會被轉(zhuǎn)換為其特定的類型个粱。
請注意:NSDecimalNumber
的值只能分配給類型為 RLMDouble
的 Realm 屬性,此外 Realm 將會存儲近似于雙精度浮點(diǎn)的數(shù)值翻翩,而不是存儲基本的十進(jìn)制數(shù)值都许。
比如說,如果我們存儲一個(gè)用戶的年齡(age)而不是存儲他們的生日嫂冻,同時(shí)還要允許當(dāng)您不知道該用戶的年齡的時(shí)候?qū)?age
屬性設(shè)置為 nil
@interface Person : RLMObject
@property NSString *name;
@property NSNumber<RLMInt> *age;
@end
@implementation Person
+ (NSArray *)requiredProperties {
return @[@"name"];
}
@end
RLMProperty
的子類屬性始終都可以為 nil胶征,因此這些類型不能夠放在 requiredProperties
中,并且 RLMArray
不支持存儲 nil
值桨仿。
簡單說明
這個(gè)表格提供了關(guān)于聲明模型屬性的簡易參考:
類型 | 非可選值形式 | 可選值形式 |
---|---|---|
Bool | @property BOOL value; | @property NSNumber<RLMBool> *value; |
Int | @property int value; | @property NSNumber<RLMInt> *value; |
Float | @property float value; | @property NSNumber<RLMFloat> *value; |
Double | @property double value; | @property NSNumber<RLMDouble> *value; |
String | @property NSString *value; | @property NSString *value; |
Data | @property NSData *value; | @property NSData *value; |
Date | @property NSDate *value; | @property NSDate *value; |
Object | n/a: 必須是可選值 | @property Object *value; |
List | @property RLMArray<Object *><Object> *value; | n/a: 必須是非可選值 |
LinkingObjects | @property (readonly) RLMLinkingObjects<Object *> *value; | n/a: 必須是非可選值 |
- Objective?C 引用類型的必需屬性必須要聲明在聯(lián)合體當(dāng)中:
@implementation MyModel
+ (NSArray *)requiredProperties {
return @[@"value"];
}
@end
- 鏈接對象屬性必須連帶
+linkingObjectsProperties
方法一同聲明:
@implementation MyModel
+ (NSDictionary *)linkingObjectsProperties {
return @{ @"property": [RLMPropertyDescriptor descriptorWithClass:Class.class propertyName:@"link"] };
}
@end
屬性特性(attributes)
注意由于 Realm 在自己的引擎內(nèi)部有很好的語義解釋系統(tǒng)睛低,所以 Objective?C 的許多屬性特性將被忽略,如 nonatomic
, atomic
, strong
, copy
和 weak
等服傍。 因此為了避免誤解暇昂,我們推薦您在編寫數(shù)據(jù)模型的時(shí)候不要使用任何的屬性特性。 當(dāng)然伴嗡,如果您已經(jīng)設(shè)置了這些屬性特性急波,那么在 RLMObject
對象被寫入 Realm 數(shù)據(jù)庫前,這些特性會一直生效瘪校。 無論 RLMObject
對象是否受到 Realm 管理澄暮,您為其編寫的自定義 getter
和 setter
方法都能正常工作。
如果您在 Swift 中使用 Objective-C 版本的 Realm 的話阱扬,模型的屬性前面需要加上 dynamic var
泣懊,這是為了讓這些屬性能夠被底層數(shù)據(jù)庫數(shù)據(jù)所訪問。
索引屬性(Indexed Properties)
重寫 +indexedProperties
方法可以為數(shù)據(jù)模型中需要添加索引的屬性建立索引:
@interface Book : RLMObject
@property float price;
@property NSString *title;
@end
@implementation Book
+ (NSArray *)indexedProperties {
return @[@"title"];
}
@end
Realm 支持字符串麻惶、整數(shù)馍刮、布爾值以及 NSDate
屬性作為索引。
對屬性進(jìn)行索引可以減少插入操作的性能耗費(fèi)窃蹋,加快比較檢索的速度(比如說 = 以及 IN 操作符)卡啰。
屬性默認(rèn)值
重寫+defaultPropertyValues
可以每次在對象創(chuàng)建之后為其提供默認(rèn)值静稻。
@interface Book : RLMObject
@property float price;
@property NSString *title;
@end
@implementation Book
+ (NSDictionary *)defaultPropertyValues {
return @{@"price" : @0, @"title": @""};
}
@end
對象的自更新特性
RLMObject
實(shí)例是底層數(shù)據(jù)的動態(tài)表現(xiàn),其會進(jìn)行自動更新匈辱,這意味著對象不需要進(jìn)行刷新振湾。修改某個(gè)對象的屬性會立刻影響到其他所有指向同一個(gè)對象的實(shí)例。
Dog *myDog = [[Dog alloc] init];
myDog.name = @"小白";
myDog.age = 1;
[realm transactionWithBlock:^{
[realm addObject:myDog];
}];
Dog *myPuppy = [[Dog objectsWhere:@"age == 1"] firstObject];
[realm transactionWithBlock:^{
myPuppy.age = 2;
}];
myDog.age; // => 2
RLMObject
的這個(gè)特性不僅讓 Realm 保證速度和效率亡脸,它同時(shí)還讓代碼更加簡潔押搪、更為靈活。比如說浅碾,如果您的 UI 代碼是基于某個(gè)特定的 Realm 對象來現(xiàn)實(shí)的大州,那么在觸發(fā) UI 重繪之前,您不用擔(dān)心數(shù)據(jù)的刷新或者重新檢索等問題垂谢。
您也可以查看 Realm 通知 一節(jié)以確認(rèn) Realm 數(shù)據(jù)何時(shí)被更新摧茴,比如說由此來決定應(yīng)用 UI 何時(shí)需要被更新。此外埂陆,還可以使用 鍵值編碼苛白,當(dāng)某個(gè) RLMObject
的特定屬性發(fā)生更新時(shí)去發(fā)送通知。
主鍵(Primary Keys)
重寫 +primaryKey
可以設(shè)置模型的主鍵焚虱。聲明主鍵之后购裙,對象將允許進(jìn)行查詢,并且更新速度更加高效鹃栽,而這也會要求每個(gè)對象保持唯一性躏率。 一旦帶有主鍵的對象被添加到 Realm 之后,該對象的主鍵將不可修改民鼓。
@interface Person : RLMObject
@property NSInteger id;
@property NSString *name;
@end
@implementation Person
+ (NSString *)primaryKey {
return @"id";
}
@end
忽略屬性(Ignored Properties)
重寫 +ignoredProperties
可以防止 Realm 存儲數(shù)據(jù)模型的某個(gè)屬性薇芝。Realm 將不會干涉這些屬性的常規(guī)操作,它們將由成員變量(ivar)提供支持丰嘉,并且您能夠輕易重寫它們的 setter
和 getter
夯到。
@interface Person : RLMObject
@property NSInteger tmpID;
@property (readonly) NSString *name; // 只讀屬性將被自動忽略
@property NSString *firstName;
@property NSString *lastName;
@end
@implementation Person
+ (NSArray *)ignoredProperties {
return @[@"tmpID"];
}
- (NSString *)name {
return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
@end
忽略屬性的行為與 Objective-C 或者 Swift 類當(dāng)中的普通對象相似。它們并不支持任何一種 Realm 特定的功能饮亏。例如耍贾,無法通過查詢來檢索忽略屬性,也無法實(shí)現(xiàn)自動更新路幸,即便另一個(gè)相同對象的實(shí)例的忽略屬性值發(fā)生了變更荐开。此外忽略屬性發(fā)生更改的時(shí)候也不會觸發(fā)通知,盡管仍然可以使用 KVO 來實(shí)現(xiàn)簡直觀察简肴。
模型繼承
Realm 允許模型能夠生成更多的子類晃听,也允許跨模型進(jìn)行代碼復(fù)用,但是由于某些 Cocoa 特性使得運(yùn)行時(shí)中豐富的類多態(tài)無法使用。以下是可以完成的操作:
父類中的類方法能扒,實(shí)例方法和屬性可以被它的子類所繼承
子類中可以在方法以及函數(shù)中使用父類作為參數(shù)
以下是不能完成的:
多態(tài)類之間的轉(zhuǎn)換(例如子類轉(zhuǎn)換成子類佣渴,子類轉(zhuǎn)換成父類,父類轉(zhuǎn)換成子類等)
同時(shí)對多個(gè)類進(jìn)行檢索
多類容器 (RLMArray 以及 RLMResults)赫粥。
向 Realm 中增加此特性已經(jīng)在規(guī)劃當(dāng)中,并且我們暫時(shí)提供了一些代碼示例予借,以便能夠?qū)ΩR姷哪J竭M(jìn)行處理越平。
另外,如果您的代碼實(shí)現(xiàn)允許的話灵迫,我們建議您使用以下模式秦叛,也就是使用類組合模式來構(gòu)建子類,以便能夠包含其他類中的相關(guān)邏輯:
// 基礎(chǔ)模型
@interface Animal : RLMObject
@property NSInteger age;
@end
@implementation Animal
@end
// 包含有 Animal 的模型
@interface Duck : RLMObject
@property Animal *animal;
@property NSString *name;
@end
@implementation Duck
@end
@interface Frog : RLMObject
@property Animal *animal;
@property NSDate *dateProp;
@end
@implementation Frog
@end
// 用法
Duck *duck = [[Duck alloc] initWithValue:@{@"animal" : @{@"age" : @(3)}, @"name" : @"Gustav" }];
集合
Realm 擁有一系列能夠幫助表示一組對象的類型瀑粥,我們稱之為「Realm 集合」:
1挣跋、RLMResults
類,表示從檢索 中所返回的對象集合狞换。
2避咆、RLMArray
類,表示模型中的對多關(guān)系修噪。
3查库、RLMLinkingObjects
類,表示模型中的反向關(guān)系黄琼。
4樊销、RLMCollection
協(xié)議,定義了所有 Realm 集合所需要遵守的常用接口脏款。
Realm 集合實(shí)現(xiàn)了 RLMCollection
協(xié)議围苫,這確保它們能夠保持一致。這個(gè)協(xié)議繼承自 NSFastEnumeration
撤师,因此它應(yīng)當(dāng)與其他 Foundation 當(dāng)中的集合用法一致剂府。 其他常用的 Realm 集合 API 也在這個(gè)協(xié)議當(dāng)中進(jìn)行了聲明,例如其中包括檢索剃盾、排序以及聚合操作周循。 RLMArray
擁有額外的修改操作,這些操作不在協(xié)議接口當(dāng)中有定義万俗,例如添加和刪除對象湾笛。
使用 RLMCollection
協(xié)議,您可以編寫能夠操作任意 Realm 集合的泛型代碼:
@implementation MyObject
- (void)operateOnCollection:(id<RLMCollection>)collection {
// collection 既可以是 RLMResults闰歪,也可以是 RLMArray
NSLog(@"對集合 %@s 進(jìn)行操作", collection.objectClassName);
}
@end
對象存儲
對對象的所有更改(添加嚎研,修改和刪除)都必須通過寫入事務(wù)(transaction)完成。
Realm 的對象可以被實(shí)例化并且作為unmanaged
對象使用(也就是還未添加到 Realm 數(shù)據(jù)庫中的對象),和其他常規(guī)Objective?C對象無異临扮。
如果您想要在多個(gè)線程中共享對象论矾,或者在應(yīng)用重啟后重復(fù)使用對象,那么您必須將其添加到 Realm 數(shù)據(jù)庫中——這個(gè)操作必須在寫入事務(wù)中完成杆勇。
因?yàn)閷懭胧聞?wù)將會產(chǎn)生不可忽略的性能消耗贪壳,因此你應(yīng)當(dāng)檢視你的代碼以確保減少寫入事務(wù)的次數(shù)。
由于寫入事務(wù)像其余硬盤讀寫操作一樣蚜退,會出現(xiàn)失敗的情況闰靴,因此 -[RLMRealm transactionWithBlock:]
以及 -[RLMRealm commitWriteTransaction]
可以選擇加上 NSError
指針參數(shù) 因此你可以處理和恢復(fù)諸如硬盤空間溢出之類的錯(cuò)誤。此外钻注,其他的錯(cuò)誤都無法進(jìn)行恢復(fù)蚂且。簡單起見,我們的代碼示例并不會處理這些錯(cuò)誤幅恋,但是您應(yīng)當(dāng)在您應(yīng)用當(dāng)中注意到這些問題杏死。
創(chuàng)建對象
當(dāng)定義完數(shù)據(jù)模型之后,您可以將您的 RLMObject
子類實(shí)例化捆交,然后向 Realm 中添加新的實(shí)例淑翼。我們以下面這個(gè)簡單的模型為例:
// 狗狗的數(shù)據(jù)模型
@interface Dog : RLMObject
@property NSString *name;
@property NSInteger age;
@end
// 實(shí)現(xiàn)文件
@implementation Dog
@end
我們可以用多種方法創(chuàng)建一個(gè)新的對象:
// (1) 創(chuàng)建一個(gè)狗狗對象,然后設(shè)置其屬性
Dog *myDog = [[Dog alloc] init];
myDog.name = @"大黃";
myDog.age = 10;
// (2) 通過字典創(chuàng)建狗狗對象
Dog *myOtherDog = [[Dog alloc] initWithValue:@{@"name" : @"豆豆", @"age" : @3}];
// (3) 通過數(shù)組創(chuàng)建狗狗對象
Dog *myThirdDog = [[Dog alloc] initWithValue:@[@"豆豆", @3]];
使用指定初始化器(designated initializer)創(chuàng)建對象是最簡單的方式品追。請注意窒舟,所有的必需屬性都必須在對象添加到 Realm 前被賦值。
通過使用恰當(dāng)?shù)逆I值诵盼,對象還可以通過字典完成創(chuàng)建惠豺。
最后,RLMObject
子類還可以通過數(shù)組完成實(shí)例化风宁,數(shù)組中的值必須和數(shù)據(jù)模型中對應(yīng)屬性的次序相同洁墙。
嵌套屬性(Nested Object)
如果某個(gè)對象中有 RLMObject
或者 RLMArray
類型的屬性,那么通過使用嵌套的數(shù)組或者字典便可以對這些屬性遞歸地進(jìn)行設(shè)置戒财。您只需要簡單的用表示其屬性的字典或者數(shù)組替換每個(gè)對象即可:
// 這里我們就可以使用已存在的狗狗對象來完成初始化
Person *person1 = [[Person alloc] initWithValue:@[@"李四", @30, @[aDog, anotherDog]]];
// 還可以使用多重嵌套
Person *person2 = [[Person alloc] initWithValue:@[@"李四", @30, @[@[@"小黑", @5],
@[@"旺財(cái)", @6]]]];
即使是數(shù)組以及字典的多重嵌套热监,Realm 也能夠輕松完成對象的創(chuàng)建。注意 RLMArray
只能夠包含 RLMObject
類型饮寞,不能包含諸如 NSString
之類的基礎(chǔ)類型孝扛。
添加數(shù)據(jù)
向 Realm 中添加數(shù)據(jù)的步驟如下:
// 創(chuàng)建對象
Person *author = [[Person alloc] init];
author.name = @"大衛(wèi)·福斯特·華萊士";
// 獲取默認(rèn)的 Realm 實(shí)例
RLMRealm *realm = [RLMRealm defaultRealm];
// 每個(gè)線程只需要使用一次即可
// 通過事務(wù)將數(shù)據(jù)添加到 Realm 中
[realm beginWriteTransaction];
[realm addObject:author];
[realm commitWriteTransaction];
等您將某個(gè)對象添加到 Realm 數(shù)據(jù)庫之后,您可以繼續(xù)使用它幽崩,并且您對其做的任何更改都會被保存(必須在一個(gè)寫入事務(wù)當(dāng)中完成)苦始。當(dāng)寫入事務(wù)提交之后,使用相同 Realm 數(shù)據(jù)源的其他線程才能夠?qū)@個(gè)對象進(jìn)行更改慌申。
請注意:如果在進(jìn)程中存在多個(gè)寫入操作的話陌选,那么單個(gè)寫入操作將會阻塞其余的寫入操作,并且還會鎖定該操作所在的當(dāng)前線程。
這個(gè)特性與其他持久化解決方案類似咨油,我們建議您使用該方案常規(guī)的最佳做法:將寫入操作轉(zhuǎn)移到一個(gè)獨(dú)立的線程中執(zhí)行您炉。
由于 Realm 采用了 MVCC 設(shè)計(jì)架構(gòu),讀取操作 并不會 因?yàn)閷懭胧聞?wù)正在進(jìn)行而受到影響役电。除非您需要立即使用多個(gè)線程來同時(shí)執(zhí)行寫入操作赚爵,不然您應(yīng)當(dāng)采用批量化的寫入事務(wù),而不是采用多次少量的寫入事務(wù)法瑟。
查看RLMRealm和RLMObject來獲得更多內(nèi)容冀膝。
更新數(shù)據(jù)
Realm 提供了一系列用以更新數(shù)據(jù)的方式,這些方式都有著各自所適應(yīng)的情景瓢谢。請選擇最符合您當(dāng)前需求的方式來使用:
內(nèi)容直接更新
您可以在寫入事務(wù)中通過設(shè)置某個(gè)對象的屬性從而完成對象的更新操作畸写。
// 在一個(gè)事務(wù)中更新對象
[realm beginWriteTransaction];
author.name = @"托馬斯·品欽";
[realm commitWriteTransaction];
通過主鍵更新
如果您的數(shù)據(jù)模型中設(shè)置了主鍵的話驮瞧,那么您可以使用+[RLMObject createOrUpdateInRealm:withValue:]
來更新對象氓扛,或者當(dāng)對象不存在時(shí)插入新的對象。
// 創(chuàng)建一個(gè)帶有主鍵的“書籍”對象论笔,作為事先存儲的書籍
Book *cheeseBook = [[Book alloc] init];
cheeseBook.title = @"奶酪食譜";
cheeseBook.price = @9000;
cheeseBook.id = @1;
// 通過 id = 1 更新該書籍
[realm beginWriteTransaction];
[Book createOrUpdateInRealm:realm withValue:cheeseBook];
[realm commitWriteTransaction];
如果主鍵 id 為1的 Book 對象已經(jīng)存在于數(shù)據(jù)庫當(dāng)中了采郎,那么對象就會簡單地進(jìn)行更新。而如果不在數(shù)據(jù)庫中存在的話狂魔,那么這個(gè)操作將會創(chuàng)建一個(gè)新的 Book 對象并添加到數(shù)據(jù)庫當(dāng)中蒜埋。
您同時(shí)通過傳遞您想要更新值的集合,從而更新帶有主鍵的某個(gè)對象的部分值最楷,比如說如下所示:
// 假設(shè)帶有主鍵值 `1` 的“書籍”對象已經(jīng)存在
[realm beginWriteTransaction];
[Book createOrUpdateInRealm:realm withValue:@{@"id": @1, @"price": @9000.0f}];
// 這本書的`title`屬性不會被改變
[realm commitWriteTransaction];
如果對象沒有定義主鍵的話整份,那么您不能夠調(diào)用出現(xiàn)在本章的這些方法(也就是那些以 OrUpdate
結(jié)尾的方法)。
請注意:當(dāng)對象更新的時(shí)候籽孙,NSNull
仍然會被認(rèn)為是可選屬性 的有效值烈评。如果您提供了一個(gè)屬性值為 NSNull
的字典,那么這些都會應(yīng)用到您的對象當(dāng)中犯建,并且對應(yīng)的屬性都將為空讲冠。為了確保不出現(xiàn)任何意外的數(shù)據(jù)丟失,請?jiān)谑褂么朔椒ǖ臅r(shí)候再三確認(rèn)只提供了您想要進(jìn)行更新的屬性适瓦。
鍵值編碼
RLMObject
竿开、RLMResult
以及 RLMArray
都遵守鍵值編碼(Key-Value Coding)(KVC)機(jī)制。 當(dāng)您在運(yùn)行時(shí)才能決定哪個(gè)屬性需要更新的時(shí)候玻熙,這個(gè)方法是最有用的否彩。
將 KVC 應(yīng)用在集合當(dāng)中是大量更新對象的極佳方式,這樣就可以不用經(jīng)常遍歷集合嗦随,為每個(gè)項(xiàng)目創(chuàng)建一個(gè)訪問器了胳搞。
RLMResults<Person *> *persons = [Person allObjects];
[[RLMRealm defaultRealm] transactionWithBlock:^{
[[persons firstObject] setValue:@YES forKeyPath:@"isFirst"];
// 將每個(gè)人的 planet 屬性設(shè)置為“地球”
[persons setValue:@"地球" forKeyPath:@"planet"];
}];
刪除數(shù)據(jù)
通過在寫入事務(wù)中將要?jiǎng)h除的對象傳遞給 -[RLMRealm deleteObject:]
方法,即可完成刪除操作。
// 存儲在 Realm 中的 cheeseBook 對象
// 在事務(wù)中刪除一個(gè)對象
[realm beginWriteTransaction];
[realm deleteObject:cheeseBook];
[realm commitWriteTransaction];
您也能夠刪除存儲在 Realm 中的所有數(shù)據(jù)肌毅。注意筷转,Realm 文件的大小不會被改變,因?yàn)樗鼤A艨臻g以供日后快速存儲數(shù)據(jù)悬而。
// 從 Realm 中刪除所有數(shù)據(jù)
[realm beginWriteTransaction];
[realm deleteAllObjects];
[realm commitWriteTransaction];