let config = Realm.Configuration(shouldCompactOnLaunch: { totalBytes, usedBytes in // totalBytes refers to the size of the file on disk in bytes (data + free space) // usedBytes refers to the number of bytes used by data in the file
// Compact if the file is over 100MB in size and less than 50% 'used' let oneHundredMB = 100 * 1024 * 1024 return (totalBytes > oneHundredMB) && (Double(usedBytes) / Double(totalBytes)) < 0.5 }) do { // Realm is compacted on the first open if the configuration block conditions were met. let realm = try Realm(configuration: config) } catch { // handle error compacting or opening Realm }
要注意的是:
实现原理就是通过创建一个文件,重新写入数据,之后再把源文件替换掉,所以,这可能是一个很耗时的操作
即使压缩的条件满足了,但如果另一个线程正在访问 Reaml, 压缩操作将会跳过不执行
删除 Realm 文件
autoreleasepool { // all Realm usage here } let realmURL = Realm.Configuration.defaultConfiguration.fileURL! let realmURLs = [ realmURL, realmURL.appendingPathExtension("lock"), realmURL.appendingPathExtension("note"), realmURL.appendingPathExtension("management") ] for URL in realmURLs { do { try FileManager.default.removeItem(at: URL) } catch { // handle error } }
数据模型
定义 Model
使用 Swift 定义的 Realm 的数据模型是通过类属性来实现,像普通 Swift 类一样使用,只要继承了 Object 类,或者另一个数据模型类
// 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. }
对于没有定义主键的模型,不可以基于主键来添加或更新数据
查询
查询操作返回一个 Results 实例
这个实例大抵与 Swift 的 Array 相同,不同之处在于,Results 中的元素类型都必须是一致的
// Query using a predicate string var tanDogs = realm.objects(Dog.self).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.self).filter(predicate)
排序
链式查询
分页读取
在其他的数据库中,通常使用 LIMIT 关键字限制一次读取的数据量,达到了分页的效果
而在 Realm 中,由于懒加载的特性,直接拿 Results 读取就可以了
// Loop through the first 5 Dog objects // restricting the number of objects read from disk let dogs = try! Realm().objects(Dog.self) for i in 0..<5 { let dog = dogs[i] // ... }
class ViewController: UITableViewController { var notificationToken: NotificationToken? = nil
override func viewDidLoad() { super.viewDidLoad() let realm = try! Realm() let results = realm.objects(Person.self).filter("age > 5")
// Observe Results Notifications notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in guard let tableView = self?.tableView else { return } switch changes { case .initial: // Results are now populated and can be accessed without blocking the UI tableView.reloadData() case .update(_, let deletions, let insertions, let modifications): // Query results have changed, so apply them to the UITableView tableView.beginUpdates() tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }), with: .automatic) tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}), with: .automatic) tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }), with: .automatic) tableView.endUpdates() case .error(let error): // An error occurred while opening the Realm file on the background worker thread fatalError("\(error)") } } }
deinit { notificationToken?.stop() } }
对象通知
Realm 支持对象级别的通知。这意味着,可以为某一个特定的 Realm 数据模型注册通知
当这个数据模型实例被删除之后,通知回调将不会在被调用
class StepCounter: Object { @objc dynamic var steps = 0 }
let stepCounter = StepCounter() let realm = try! Realm() try! realm.write { realm.add(stepCounter) } var token : NotificationToken? token = stepCounter.addNotificationBlock { change in switch change { case .change(let properties): for property in properties { if property.name == "steps" && property.newValue as! Int > 1000 { print("Congratulations, you've exceeded 1000 steps.") token = nil } } case .error(let error): print("An error occurred: \(error)") case .deleted: print("The object was deleted.") } }
// Add fine-grained notification block token = collection.addNotificationBlock { changes in switch changes { case .initial: tableView.reloadData() case .update(_, let deletions, let insertions, let modifications): // Query results have changed, so apply them to the UITableView tableView.beginUpdates() tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }), with: .automatic) tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}), with: .automatic) tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }), with: .automatic) tableView.endUpdates() case .error(let error): // handle error () } }
func insertItem() throws { // Perform an interface-driven write on the main thread: collection.realm!.beginWrite() collection.insert(Item(), at: 0) // And mirror it instantly in the UI tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic) // Making sure the change notification doesn't apply the change a second time try collection.realm!.commitWrite(withoutNotifying: [token]) }