Realm Swift
Realm Swift
Realm支持类型
Realm ObjectModel Properties属性声明方式
注意事项:
Relationships一对一(To-One Relationships)class Dog: Object {
// ... other property declarations
dynamic var owner: Person? // to-one relationships must be optional
}
一对多(To-Many Relationships)class Person: Object {
// ... other property declarations
let dogs = List<Dog>()
}
反向链接(Inverse Relationships / backlink)class Dog: Object {
dynamic var name = ""
dynamic var age = 0
var owners: [Person] {
// Realm 并不会存储这个属性,因为这个属性只定义了 getter
// 定义“owners”,和 Person.dogs 建立反向关系
return linkingObjects(Person.self,forProperty: "dogs")
}
}
Indexed Properties添加索引属性,加快查询速度. class Book: Object {
dynamic var price = 0
dynamic var title = ""
override static func indexedProperties() -> [String] {
return ["title"]
}
}
索引支持类型
注意事项
Auto-Updating Objects顾名思义当一个数据的内容改变时,它会自动更新该数据的所有实例化对象 let myDog = Dog()
myDog.name = "Fido"
myDog.age = 1
try! realm.write {
realm.add(myDog)
}
let myPuppy = realm.objects(Dog).filter("age == 1").first
try! realm.write {
myPuppy!.age = 2
}
print("age of my dog: (myDog.age)") // => 2
当数据变化,需要更新界面时,需要配合 [Realm notifications](#Realm notifications) 或 [key-value observation](#key-value observation)实现,后续会详细描述这2个功能 Primary Keys声明主键之后,对象将被允许查询,更新速度更加高效,并且要求每个对象保持唯一性。 class Person: Object {
dynamic var id = 0
dynamic var name = ""
override static func primaryKey() -> String? {
return "id"
}
}
Ignored Properties重写 Object.ignoredProperties() 可以防止 Realm 存储数据模型的某个属性。Realm 将不会干涉这些属性的常规操作,它们将由成员变量(ivar)提供支持,并且您能够轻易重写它们的 setter 和 getter。 class Person: Object {
dynamic var tmpID = 0
var name: String { // read-only properties are automatically ignored
return "(firstName) (lastName)"
}
dynamic var firstName = ""
dynamic var lastName = ""
override static func ignoredProperties() -> [String] {
return ["tmpID"]
}
}
Writes所有写入操作(添加,修改,删除)都必须依托一个write transaction. Creating Objects数据赋值方式: // (1) Create a Dog object and then set its properties var myDog = Dog() myDog.name = "Rex" myDog.age = 10 // (2) Create a Dog object from a dictionary let myOtherDog = Dog(value: ["name" : "Pluto","age": 3]) // (3) Create a Dog object from an array let myThirdDog = Dog(value: ["Fido",5])
Nested Objects嵌套赋值方式: // Instead of using already existing dogs...
let aPerson = Person(value: ["Jane",30,[aDog,anotherDog]])
// ...we can create them inline
let anotherPerson = Person(value: ["Jane",[["Buster",5],["Buddy",6]]])
Adding Objectswrite操作是阻塞操作,如果有一个写操作,那么其他线程的write操作都会被阻塞. // Create a Person object let author = Person() author.name = "David Foster Wallace" // Get the default Realm let realm = try! Realm() // You only need to do this once (per thread) // Add to the Realm inside a transaction try! realm.write { realm.add(author) }
Updating ObjectsTyped Updates// Update an object with a transaction
try! realm.write {
author.name = "Thomas Pynchon"
}
Creating and Updating Objects With Primary Keysupdate:true Object必须具有PrimaryKeys.否则会报错. // Creating a book with the same primary key as a previously saved book
let cheeseBook = Book()
cheeseBook.title = "Cheese recipes"
cheeseBook.price = 9000
cheeseBook.id = 1
// Updating book with id = 1
try! realm.write {
realm.add(cheeseBook,update: true)
}
// Assuming a "Book" with a primary key of `1` already exists.
try! realm.write {
realm.create(Book.self,value: ["id": 1,"price": 9000.0],update: true)
// the book's `title` property will remain unchanged.
}
Key-Value Codinglet persons = realm.objects(Person)
try! realm.write {
persons.first?.setValue(true,forKeyPath: "isFirst")
// set each person's planet property to "Earth"
persons.setValue("Earth",forKeyPath: "planet")
}
Deleting Objects// let cheeseBook = ... Book stored in Realm
try! realm.write {
// Delete an object with a transaction
realm.delete(cheeseBook)
realm.delete(List<T:Object>)
realm.delete(Results<T:Object>)
// Delete all objects from the realm
realm.deleteAll()
}
Queries通过查询操作,Realm 将会返回包含 Object 集合的Results实例。Results 的表现和 Array 十分相似,并且包含在 Results 中的对象能够通过索引下标进行访问。 基本查询语句 let dogs = realm.objects(Dog) // retrieves all Dogs from the default Realm
Filtering条件查询 // Query using a predicate string
var tanDogs = realm.objects(Dog).filter("color = 'tan' AND name BEGINSWITH 'B'")
// Query using an NSPredicate
let predicate = NSPredicate(format: "color = %@ AND name BEGINSWITH %@","tan","B")
tanDogs = realm.objects(Dog).filter(predicate)
var tanDogs = realm.objects(Dog).filter("color = 'tan'").filter("name BEGINSWITH 'B'")
支持的断言类型
Sorting// Sort tan dogs with names starting with "B" by name
let sortedDogs = realm.objects(Dog).filter("color = 'tan' AND name BEGINSWITH 'B'").sorted("name")
//倒序
let sortedDogs = realm.objects(Dog).filter("color = 'tan' AND name BEGINSWITH 'B'").sorted("name",ascending:false)
Auto-Updating Results结果会自动更新 let puppies = realm.objects(Dog).filter("age < 2")
puppies.count // => 0
try! realm.write {
realm.create(Dog.self,value: ["name": "Fido","age": 1])
}
puppies.count // => 1
Limiting Results// Loop through the first 5 Dog objects
// restricting the number of objects read from disk
let dogs = try! Realm().objects(Dog)
for i in 0..<5 {
let dog = dogs[i]
// ...
}
RealmsRealm ConfigurationRealm.Configuration.defaultConfiguration = config.直接设置默认配置 func setDefaultRealmForUser(username: String) {
var config = Realm.Configuration()
// Use the default directory,but replace the filename with the username
config.path = NSURL.fileURLWithPath(config.path!)
.URLByDeletingLastPathComponent?
.URLByAppendingPathComponent("(username).realm")
.path
// Set this as the configuration used for the default Realm
Realm.Configuration.defaultConfiguration = config
}
Other Realms指定BundleData中的Realm let config = Realm.Configuration(
// Get the path to the bundled file
path: NSBundle.mainBundle().pathForResource("MyBundledData",ofType:"realm"),// Open the file in read-only mode as application bundles are not writeable
readOnly: true)
// Open the Realm with the configuration
let realm = try! Realm(configuration: config)
// Read some data from the bundled Realm
let results = realm.objects(Dog).filter("age > 5")
注意如果是初始化一个Realm,指定的路径必须是可写的 In-Memory Realms内存中的Realms,没有保存在磁盘上. 注意由于ARC的原因,内存Realms创建的数据必须要有一个强引用,否则会被回收 Error Handling错误只会发生在第一次创建Realms.如果Realms已经创建,以后不会发生错误. do {
let realm = try Realm()
} catch let error as NSError {
// handle error
}
Copying Objects Between Realms数据拷贝只能是不同Realms都在同一线程中创建的,否则无法实现数据拷贝 realm.create(MyObjectSubclass.self,value: originalObjectInstance)
Auxiliary Realm FilesRealms内部处理的辅助文件,对于使用者来说,就是汇报bug的时候,需要一并提交这些文件
Class SubsetsRealms可以配置只保存特定的Class,除指定的Class外,其他Class一律不存储. let config = Realm.Configuration(objectTypes: [MyClass.self,MyOtherClass.self])
let realm = try! Realm(configuration: config)
Deleting Realm Files删除本地Realm autoreleasepool {
// all Realm usage here
}
let manager = NSFileManager.defaultManager()
let realmPath = Realm.Configuration.defaultConfiguration.path as! NSString
let realmPaths = [
realmPath as String,realmPath.stringByAppendingPathExtension("lock")!,realmPath.stringByAppendingPathExtension("log_a")!,realmPath.stringByAppendingPathExtension("log_b")!,realmPath.stringByAppendingPathExtension("note")!
]
for path in realmPaths {
do {
try manager.removeItemAtPath(path)
} catch {
// handle error
}
}
Using Realm with Background App Refresh这章主要是说如何使用IOS本地加密,详情查看官方文档. Threading这一章主要是讲多线程开发,大量写入事务最好是放在其他线程中,以防止UI线程被阻塞 Realm为了更好的支持多线程处理,它为每个线程都创建了一个视图(SQL中的视图概念??).由于每个线程都有自己的snapshots,导致线程之间同步问题. 唯一需要记住的是:你不能在多个线程之间共享同一个Realm对象.如果这样的话,就会导致一个线程上修改了数据,其他线程无法同步数据. Seeing Changes From Other Threads在UI线程或者其他添加Runloop的线程上,Realm都会自动更新其他线程Runloop的操作结果.(这里是说其他线程有更新,UI线程或Runloop线程都不会更新数据)
在其他类型的线程上操作,都是基于Snapshots. UI线程或者其他添加Runloop的线程上,数据都会自动刷新,除非将Realm.autorefresh设置为NO 最好是不要经常性的手动调用refresh(),当你正在刷新,其他线程有其他事务进行处理时,会导致数据”pinned”,进而增大Realm在磁盘上的空间 ```
###Passing Instances Across Threads
继承于NSObject的类,是可以在线程之间传递的.<BR>
继承于Realm,Object,Results,or List的类,是无法在线程之间传递的.否则会引起崩溃.<BR>
多线程之间传递数据的解决方案:<BR>
* Object:可以通过primary key来实现.
* Results:可以filter或者NSPredicate来实现.
**Realm一些可以多线程操作的属性和方法,如下**
* Realm: all properties,class methods,and initializers.
* Object: invalidated,objectSchema,realm,and initializers.
* Results: objectClassName and realm.
* List: invalidated,objectClassName,and realm.
###Using a Realm Across Threads
**想要在不同线程中,访问同一个Realm文件,就必须要在各自线程中获取相同配置的Realm实例.就是重新调用Realm.realm().**<BR>
let queue = dispatch_queue_create(“test”,DISPATCH_QUEUE_CONCURRENT); // Break up the writing blocks into smaller portions // by starting a new transaction for idx1 in 0..<1000 { realm.beginWrite() // Add row via dictionary. Property order is ignored. for idx2 in 0..<1000 { realm.create(Person.self,value: [ "name": "(idx1)","birthdate": NSDate(timeIntervalSince1970: NSTimeInterval(idx2)) ]) } // Commit the write transaction // to make this data available to other threads try! realm.commitWrite() } } ##JSON
不支持json数据直接导入,但是支持 NSJSONSerialization.JSONObjectWithData(_:options:)转换的导入.<BR>
// A Realm Object that represents a city // Insert from NSData containing JSON ######注意事项
* float properties should be initialized with float-backed NSNumbers(float类型需要使用NSNumber来声明)* * * * * * * *
* NSDate and NSData properties cannot be automatically inferred from strings,but should be converted to the appropriate type before passing to Realm().create(_:value:update:).(**没理解**)
* If a JSON null (i.e. NSNull) is supplied for a required property,an exception will be thrown.(如果一个json对象为null,会抛出异常)
* If no property is supplied on insert for a required property,an exception will be thrown.(如果一个声明属性,没有对应的值,会抛出异常)
* Realm will ignore any properties in the JSON not defined by the Object.(当Json对象中有Object没有声明变量,会忽略)
##Notifications
多线程中对特定数据有修改时,会发送Notifications.<BR>
**需要注意的是,addNotificationBlock返回的Token是必须要被强引用的,否则无法回调**<BR>
支持的调用方式:
* Realm.addNotificationBlock(_:)
* AnyRealmCollection.addNotificationBlock(_:)
* Results.addNotificationBlock(_:)
* List.addNotificationBlock(_:)
* NotificationToken.stop()
// Observe Realm Notifications // later // Observe Results Notifications // later ##Key-Value Observation
**这章略过,由于不熟悉KVO,所以先不学习这章**<BR>
Realm objects are Key-Value Observing compliant for most properties. All persisted (non-ignored) properties on your Object subclasses are KVO-compliant,along with the invalidated property on Object and List.
Observing properties of standalone instances of Object subclasses works just like with any other dynamic property,but note that you cannot add an object to a Realm (with realm.add(obj) or other similar methods) while it has any registered observers.
Observing properties of persisted objects works a little differently. With persisted objects,there are three times when the value of a property may change: when you directly assign to it; when you call realm.refresh() or the Realm is automatically refreshed after a write transaction is committed on a different thread; and when you call realm.beginWrite() after changes on a different thread which have not been picked up by a refresh on the current thread.
In the latter two cases,all of the changes made in the write transaction(s) on another thread will be applied at once,and KVO notifications will all be sent at once. Any intermediate steps are discarded,so if in the write transaction you incremented a property from one to ten,on the main thread you’ll get a single notification of a change directly from one to ten. Because properties can change in value when not in a write transaction or even as part of beginning a write transaction,trying to modify persisted Realm objects from within observeValueForKeyPath(_:ofObject:change:context:) is not recommended.
Unlike NSMutableArray properties,observing changes made to List properties does not require using mutableArrayValueForKey(_:),although that is supported for compatiblity with things not written for Realm. Instead,you can simply call the modification methods on List directly,and anyone observing the property it is stored in will be notified. List properties do not need to be marked as dynamic to be observable,unlike normal properties.
In our example apps you can find a short example of using Realm with ReactiveCocoa from Objective?C,and ReactKit from Swift.
##Migrations
**数据迁移,版本迭代时,数据库常用**<BR>
###为什么要进行数据库迁移
class Person: Object { 在某个版本更新中,变成了下边这样 class Person: Object { 那么就需要用到数据迁移了.
###Performing a Migration
Realm.Configuration.defaultConfiguration.schemaVersion = 2; }; ###Adding more versions
Realm.Configuration.defaultConfiguration.schemaVersion = 2; // Add the `email` property to Realms with a schema version of 0 or 1 if oldSchemaVersion < 2 { newObject!["email"] = "" } }) } }; ###Linear Migrations
需要考虑跨版本的数据库迁移,例如v0直接升级到v3版本,而不是只考虑v2升级到v3.
##Encryption
**Realm的加密只支持OS X,IOS,WatchKit.但是不支持watchOS**<BR> Realm的加密方式为:**key为64字节,AES-256+SHA2**<BR> **加密过的 Realm 只会带来很少的额外资源占用(通常最多只会比平常慢10%)**<BR> **注:如果数据库加密后,由于不知道加密方式,即使有原始key,也无法获取解密key,所以无法用Realm Browser查看.**
**注:如果数据库加密,每次获取Realm实例时,必须使用encryptionKey.**
func getKey() -> NSData { // Identifier for our keychain entry - should be unique for your application let keychainIdentifier = "io.Realm.Test" let keychainIdentifierData = keychainIdentifier.dataUsingEncoding(NSUTF8StringEncoding,allowLossyConversion: false)! // First check in the keychain for an existing key var query: [NSString: AnyObject] = [ kSecClass: kSecClassKey,kSecAttrApplicationTag: keychainIdentifierData,kSecAttrKeySizeInBits: 512,kSecReturnData: true ] // To avoid Swift optimization bug,should use withUnsafeMutablePointer() function to retrieve the keychain item // See also: http://stackoverflow.com/questions/24145838/querying-ios-keychain-using-swift/27721328#27721328 var dataTypeRef: AnyObject? var status = withUnsafeMutablePointer(&dataTypeRef) { SecItemCopyMatching(query,UnsafeMutablePointer($0)) } if status == errSecSuccess { return dataTypeRef as! NSData } // No pre-existing key from this application,so generate a new one let keyData = NSMutableData(length: 64)! let result = SecRandomCopyBytes(kSecRandomDefault,64,UnsafeMutablePointer<UInt8>(keyData.mutableBytes)) assert(result == 0,"Failed to get random bytes") // Store the key in the keychain query = [ kSecClass: kSecClassKey,kSecValueData: keyData ] status = SecItemAdd(query,nil) assert(status == errSecSuccess,"Failed to insert the new key in the keychain") return keyData } “` (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |