Realm Swift
当前这个翻译,主要是方便我自己查阅api,有很多地方写的比较晦涩或者没有翻译,敬请谅解
version 0.98.7
官方文档
参考文献
Realm支持类型
- String,NSString
- Int
- Int8,Int16,Int32,Int64
- Float
- Double
- Bool
- NSData
- NSDate
- RealmOptional
- Object
- List
Realm Object
Model Properties
属性声明方式
Class => T:Object
Type | Non-optional | Optional |
---|---|---|
声明类型 | 非可选类型声明方式 | 可选类型声明方式 |
Bool | dynamic var value = false | let value = RealmOptional() |
Int | dynamic var value = 0 | let value = RealmOptional() |
Float | dynamic var value:Float = 0.0 | let value = RealmOptional() |
Double | dynamic var value:Double = 0 | let value = RealmOptional() |
String | dynamic var value = “” | dynamic var value: String? = nil |
Data | dynamic var value = Data() | dynamic var value: NSData? = nil |
Date | dynamic var value = Date() | dynamic var value: Date? = nil |
Object | n/a: must be optional | dynamic var value: Class? |
List | let value = List | n/a: must be non-optional |
注意事项:
- Object只能声明成可选类型.
- List和RealmOptional只能声明成非可选类型.
- 使用var修饰变量,必须要使用dynamic.具体原因请查照)
Relationships
一对一(To-One Relationships)
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
一对多(To-Many Relationships)
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
反向链接(Inverse Relationships / backlink)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Indexed Properties
添加索引属性,加快查询速度.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
索引支持类型
- strings
- integers
- booleans
- NSDate
注意事项
Indexing a property will greatly speed up queries where the property is compared for equality (i.e. the = and IN operators), at the cost of slower insertions.
使用索引增加查询速度的代价是插入数据时速度会降低
Auto-Updating Objects
顾名思义当一个数据的内容改变时,它会自动更新该数据的所有实例化对象
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
当数据变化,需要更新界面时,需要配合 [Realm notifications](#Realm notifications) 或 [key-value observation](#key-value observation)实现,后续会详细描述这2个功能
Primary Keys
声明主键之后,对象将被允许查询,更新速度更加高效,并且要求每个对象保持唯一性。
一旦带有主键的对象被添加到 Realm 之后,该对象的主键将不可修改。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Ignored Properties
重写 Object.ignoredProperties() 可以防止 Realm 存储数据模型的某个属性。Realm 将不会干涉这些属性的常规操作,它们将由成员变量(ivar)提供支持,并且您能够轻易重写它们的 setter 和 getter。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
Writes
所有写入操作(添加,修改,删除)都必须依托一个write transaction.
由于write transaction会占用一定的资源,所以尽量精简write transaction的个数.当队列写入时,只需要一个就write transaction
Creating Objects
数据赋值方式:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Nested Objects
嵌套赋值方式:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
Adding Objects
write操作是阻塞操作,如果有一个写操作,那么其他线程的write操作都会被阻塞.
最好是将write操作放在一个单独的线程中.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
Updating Objects
Typed Updates
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
Creating and Updating Objects With Primary Keys
update:true Object必须具有PrimaryKeys.否则会报错.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
Key-Value Coding
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
Deleting Objects
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
Queries
通过查询操作,Realm 将会返回包含 Object 集合的Results实例。Results 的表现和 Array 十分相似,并且包含在 Results 中的对象能够通过索引下标进行访问。
所有的查询(包括查询和属性访问)在 Realm 中都是延迟加载的,只有当属性被访问时,才能够读取相应的数据。也就是说当没有使用数据前,进行多次排序或者过滤都是不需要额外cpu时间的
查询结构不是Copy对象,而是引用对象.所以在Write操作中修改查询数据,是直接修改数据库中的数据.
基本查询语句
- 1
- 2
- 1
- 2
Filtering
条件查询
类似NSPredicate,同时支持NSPredicate.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
支持的断言类型
- 比较操作数(comparison operand)可以是属性名称或者某个常量,但至少有一个操作数必须是属性名称;
- 比较操作符 ==、<=、<、>=、>、!=, 以及 BETWEEN 支持 int、long、long long、float、double 以及 NSDate 属性类型的比较,比如说 age == 45
- 相等比较 ==以及!=,比如说Results().filter(“company == %@”, company)
- 比较操作符 == and != 支持布尔属性
- 对于 NSString 和 NSData 属性来说,我们支持 ==、!=、BEGINSWITH、CONTAINS 以及 ENDSWITH 操作符,比如说 name CONTAINS ‘Ja’
- 字符串支持忽略大小写的比较方式,比如说 name CONTAINS[c] ‘Ja’ ,注意到其中字符的大小写将被忽略
- **Realm 支持以下复合操作符:“AND”、“OR” 以及 “NOT”。比如说 name BEGINSWITH ‘J’ AND age >= 32;
包含操作符 IN,比如说 name IN {‘Lisa’, ‘Spike’, ‘Hachi’}** - ==、!=支持与 nil 比较,比如说 Results().filter(“ceo == nil”)。注意到这只适用于有关系的对象,这里 ceo 是 Company 模型的一个属性
- ANY 比较,比如说 ANY student.age < 21
- 支持复合表达式类型@count, @min, @max, @sum and @avg.例如realm.objects(Company).filter(“employees.@count > 5”)
- 子查询有如下限制
-
- @count is the only operator that may be applied to the SUBQUERY expression
-
- The SUBQUERY(…).@count expression must be compared with a constant
-
- Correlated subqueries are not yet supported
Sorting
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
Auto-Updating Results
结果会自动更新
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
Limiting Results
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
Realms
Realm Configuration
Realm.Configuration.defaultConfiguration = config.直接设置默认配置
假设需要快速切换账户,可以使用一下代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
Other Realms
指定BundleData中的Realm
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
注意
如果是初始化一个Realm,指定的路径必须是可写的
In-Memory Realms
内存中的Realms,没有保存在磁盘上.
优点:可以快速的访问数据,而不需要考虑数据持久化的性能开销.内存Realms只会在temp路径里存放几个文件,用来进行线程间数据同步,不会将Realms中任何数据写入磁盘中
注意
由于ARC的原因,内存Realms创建的数据必须要有一个强引用,否则会被回收
Error Handling
错误只会发生在第一次创建Realms.如果Realms已经创建,以后不会发生错误.
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
Copying Objects Between Realms
数据拷贝只能是不同Realms都在同一线程中创建的,否则无法实现数据拷贝
- 1
- 2
- 1
- 2
Auxiliary Realm Files
Realms内部处理的辅助文件,对于使用者来说,就是汇报bug的时候,需要一并提交这些文件
- .realm.lock - A lock file for resource locks.
- .realm.log_a, .realm.log_b - Log files for transaction logs.
- .realm.note - A named pipe for notifications.
Class Subsets
Realms可以配置只保存特定的Class,除指定的Class外,其他Class一律不存储.
- 1
- 2
- 3
- 1
- 2
- 3
Deleting Realm Files
删除本地Realm
Realm在使用的时候,都是强引用,如果需要的话,就用autoreleasepool来包含
如果使用强引用,直接删除也不会有什么影响
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
Using Realm with Background App Refresh
这章主要是说如何使用iOS本地加密,详情查看官方文档.
Threading
这一章主要是讲多线程开发,大量写入事务最好是放在其他线程中,以防止UI线程被阻塞
只在一个线程中处理所有事情,不需要担心并发和多线程.(然并卵的话)
Realm在多线程处理上不需要使用线程锁,只需要注意写入操作需要在Write事件中.
Realm为了更好的支持多线程处理,它为每个线程都创建了一个视图(SQL中的视图概念??).由于每个线程都有自己的snapshots,导致线程之间同步问题.
唯一需要记住的是:你不能在多个线程之间共享同一个Realm对象.如果这样的话,就会导致一个线程上修改了数据,其他线程无法同步数据.
Seeing Changes From Other Threads
在UI线程或者其他添加Runloop的线程上,Realm都会自动更新其他线程Runloop的操作结果.(这里是说其他线程有更新,UI线程或Runloop线程都不会更新数据)
在其他类型的线程上操作,都是基于Snapshots.
所以最好的处理方法是,保存唯一的一个视图,这样就不用担心多线程并发的问题.
UI线程或者其他添加Runloop的线程上,数据都会自动刷新,除非将Realm.autorefresh设置为NO
其他类型的线程,都是以最后一次修改成功的Realm为snapshot,除非是手动refresh
Realm.commitWrite后,Realm会刷新一次
最好是不要经常性的手动调用refresh(),当你正在刷新,其他线程有其他事务进行处理时,会导致数据”pinned”,进而增大Realm在磁盘上的空间
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
let queue = dispatch_queue_create(“test”, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue) {
autoreleasepool {
// Get realm and table instances for this thread
let realm = try! Realm()
// 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()
}
}
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
// A Realm Object that represents a city
class City: Object {
dynamic var city = “”
dynamic var id = 0
// other properties left out …
}
// Insert from NSData containing JSON
try! realm.write {
let json = try! NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
realm.create(City.self, value: json, update: true)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
// Observe Realm Notifications
let token = realm.addNotificationBlock { notification, realm in
viewController.updateUI()
}
// later
token.stop()
// Observe Results Notifications
let token = realm.objects(Person).filter(“age > 5”).addNotificationBlock { results, error in
// results is identical to ‘realm.objects(Person).filter(“age > 5”)’
viewController.updateUI()
}
// later
token.stop()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
class Person: Object {
dynamic var firstName = “”
dynamic var lastName = “”
dynamic var age = 0
}
- 1
- 2
- 1
- 2
class Person: Object {
dynamic var fullName = “”
dynamic var age = 0
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
Realm.Configuration.defaultConfiguration.schemaVersion = 2;
Realm.Configuration.defaultConfiguration.migrationBlock = {migration, oldSchemaVersion in
if oldSchemaVersion < 1 {
migration.enumerate(Person.className(), { (oldObject, newObject) in
let firstName = oldObject![“firstName”] as! String
let lastName = oldObject![“lastName”] as! String
newObject![“fullName”] = “(firstName) (lastName)”
})
}
};
- 1
- 2
- 3
- 1
- 2
- 3
Realm.Configuration.defaultConfiguration.schemaVersion = 2;
Realm.Configuration.defaultConfiguration.migrationBlock = {migration, oldSchemaVersion in
if oldSchemaVersion < 10 {
migration.enumerate(Person.className(), { (oldObject, newObject) in
if oldSchemaVersion < 1 {
let firstName = oldObject![“firstName”] as! String
let lastName = oldObject![“lastName”] as! String
newObject![“fullName”] = “(firstName) (lastName)”
}
// Add the `email` property to Realms with a schema version of 0 or 1
if oldSchemaVersion < 2 {
newObject!["email"] = ""
}
})
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
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,
kSecAttrApplicationTag: keychainIdentifierData,
kSecAttrKeySizeInBits: 512,
kSecValueData: keyData
]
status = SecItemAdd(query, nil)
assert(status == errSecSuccess, "Failed to insert the new key in the keychain")
return keyData
}
“`