[Realm-Swift] 数据库的使用详解

swift开发需要应用数据库,fmdb笔记繁琐,在swift中准备放弃使用,看到了 Realm这个三方框架很强大,而且使用简单,就拿过来研究一下,感谢Swift - Realm数据库的使用详解(附样例) 这篇文章,结合Realm官网知识Realm官网知识及GitHub的demoRealmCooa,可以熟练使用,以此做个记录,以防下次遗忘

1、什么是RealmRealm

于2014 年7月发布,是一个跨平台的移动数据库引擎,专门为移动应用的数据持久化而生。其目的是要取代 Core Data 和 SQLite。

2、关于Realm,你要知道下面几点:

(1)使用简单,大部分常用的功能(比如插入、查询等)都可以用一行简单的代码轻松完成,学习成本低。
(2)Realm 不是基于 Core Data,也不是基于 SQLite 封装构建的。它有自己的数据库存储引擎。
(3)Realm 具有良好的跨平台特性,可以在 iOS 和 Android 平台上共同使用。代码可以使用 Swift 、 Objective-C 以及 Java 语言来编写。
(4)Realm 还提供了一个轻量级的数据库查看工具(Realm Browser)。你也可以用它进行一些简单的编辑操作(比如插入和删除操作)

3、支持的类型

(1)Realm 支持以下的属性类型:Bool、Int8、Int16、Int32、Int64、Double、Float、String、Date(精度到秒)以及Data.
(2)也可以使用 List<object> 和 Object 来建立诸如一对多、一对一之类的关系模型,此外 Object 的子类也支持此功能。

4、Realm的安装配置

(1)先去 Realm 的官网去下载最新框架:http://static.realm.io/downloads/swift/latest
(2)拖拽 RealmSwift.framework 和 Realm.framework 文件到”Embedded Binaries”选项中。选中 Copy items if needed 并点击 Finish

Realm-Swift数据库的使用详解

5、将数据插入到数据库中

下面代码判断默认数据库中是否有数据,如果没有的话将几个自定义对像插入到数据库中。
(1)这里以个人消费记录为例,我们先定义消费类别类,和具体消费记录类

import Foundation
import RealmSwift
 
//消费类型
class ConsumeType:Object {
    //类型名
    dynamic var name = ""
}
 
//消费条目
class ConsumeItem:Object {
    //条目名
    dynamic var name = ""
    //金额
    dynamic var cost = 0.00
    //时间
    dynamic var date = Date()
    //所属消费类别
    dynamic var type:ConsumeType?
}

(2)判断数据库记录是否为空,空的话则插入数据库(这里以默认数据库为例)

import UIKit
import RealmSwift
 
class ViewController: UIViewController {
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //使用默认的数据库
        let realm = try! Realm()
        //查询所有的消费记录
        let items = realm.objects(ConsumeItem.self)
        //已经有记录的话就不插入了
        if items.count>0 {
            return
        }
         
        //创建两个消费类型
        let type1 = ConsumeType()
        type1.name = "购物"
        let type2 = ConsumeType()
        type2.name = "娱乐"
         
        //创建三个消费记录
        let item1 = ConsumeItem(value: ["买一台电脑",5999.00,Date(),type1]) //可使用数组创建
         
        let item2 = ConsumeItem()
        item2.name = "看一场电影"
        item2.cost = 30.00
        item2.date = Date(timeIntervalSinceNow: -36000)
        item2.type = type2
         
        let item3 = ConsumeItem()
        item3.name = "买一包泡面"
        item3.cost = 2.50
        item3.date = Date(timeIntervalSinceNow: -72000)
        item3.type = type1
         
        // 数据持久化操作(类型记录也会自动添加的)
        try! realm.write {
            realm.add(item1)
            realm.add(item2)
            realm.add(item3)
        }
         
        //打印出数据库地址
        print(realm.configuration.fileURL)
    }
     
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

6、Data类型数据的存取

1.实现原理
(1)Realm 支持 Data 类型的属性,我们要做的就是将图片转换为 Data 类型,再进行存储即可。
(2)Data 类型的属性读取操作同其他数据类型的读取没什么差别。只要注意不要超过 16MB 即可。
2.实现
(1)点击“保存”按钮,将项目中的 abc.png 这张图片存储到 Realm 数据库中。
(2)点击“读取”按钮,从库中取出最新保存的那张图片,并显示在 imageview 中。
3.样例代码

import UIKit
import RealmSwift
 
class ViewController: UIViewController {
    //用于显示图片
    @IBOutlet weak var imageView: UIImageView!
     
    //使用默认的数据库
    let realm = try! Realm()
     
    override func viewDidLoad() {
        super.viewDidLoad()
    }
     
    //点击保存
    @IBAction func saveData(_ sender: Any) {
        //获取图片并转换为Data
        let imageURL =  Bundle.main.url(forResource: "0", withExtension: "png")!
        let imageData = try! Data(contentsOf: imageURL)
        //将Data数据放到实体对象中
        let portrait = HeadPortrait()
        portrait.data = imageData
        //数据持久化操作(类型记录也会自动添加的)
        try! realm.write {
            realm.add(portrait)
        }
        print("数据保存完毕!")
    }
     
    //点击加载
    @IBAction func loadData(_ sender: Any) {
        //获取所有头像图片(根据插入时间倒序排列)
        let portraits = realm.objects(HeadPortrait.self).sorted(byKeyPath: "date", ascending: false)
        //将最新一张图片显示出来
        if portraits.count > 0 {
            if let imgData = portraits[0].data {
                self.imageView.image = UIImage(data: imgData)
            }
        }
         print("数据读取完毕!")
    }
     
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
 
//用户头像
class HeadPortrait:Object {
    //图片数据
    dynamic var data:Data?
     
    //创建时间
    dynamic var date = Date()
}

7、使用Realm Browser查看数据库

(1)默认数据库是应用的 Documents 文件夹下的一个名为“default.realm”。

//打印出数据库地址

print(realm.configuration.fileURL)

(2)使用 Realm Browser 工具可以很方便的对.realm数据库进行读取和编辑(在 App Store 中搜索 Realm Browser 即可下载)。
可以看到,上面的几个对象已经成功的插入到数据库中来。

8、从数据库中读取记录并显示到表格中来

(1)通过查询操作,Realm 将会返回包含 Object 集合的 Results 实例。Results 的表现和 Array 十分相似,并且包含在 Results 中的对象能够通过索引下标进行访问。
(2)所有的查询(包括查询和属性访问)在 Realm 中都是延迟加载的,只有当属性被访问时,才能够读取相应的数据。
(3)查询结果并不是数据的拷贝:修改查询结果(在写入事务中)会直接修改硬盘上的数据。
下面我们把库里的数据加载出来,并通过表格显示出来。
效果图如下:

代码如下:

import UIKit
import RealmSwift
 
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource  {
     
    @IBOutlet weak var tableView: UITableView!
     
    var dformatter = DateFormatter()
     
    //保存从数据库中查询出来的结果集
    var consumeItems:Results<ConsumeItem>?
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        self.dformatter.dateFormat = "MM月dd日 HH:mm"
         
        self.tableView!.delegate = self
        self.tableView!.dataSource = self
        //创建一个重用的单元格
        self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
         
        //使用默认的数据库
        let realm = try! Realm()
        //查询所有的消费记录
        consumeItems = realm.objects(ConsumeItem.self)
    }
     
    //在本例中,只有一个分区
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1;
    }
     
    //返回表格行数(也就是返回控件数)
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.consumeItems!.count
    }
     
    //创建各单元显示内容(创建参数indexPath指定的单元)
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
        -> UITableViewCell {
        //同一形式的单元格重复使用,在声明时已注册
        let cell = UITableViewCell(style: .value1, reuseIdentifier: "MyCell")
        let item = self.consumeItems![indexPath.row]
        cell.textLabel?.text = item.name + " ¥" + String(format: "%.1f", item.cost)
        cell.detailTextLabel?.text = self.dformatter.string(from: item.date)
        return cell
    }
     
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

9、查询前N条数据Realm无法直接限制查询数量。所以我们如果想要查出部分数据(比如前5条记录),也是全部查出来后在结果集中捞取。

//查询并取出前5条数据
let dogs = try! Realm().objects(Dog.self)
for i in 0..<5 {
    let dog = dogs[i]
    // ...
}

Realm为何无法限制查询数量?通常查询数据库数据时,我们可以在sql语句中添加一些限制语句(比如rownum,limit,top等)来限制返回的结果集的行数。但我们使用Realm会发现,它没有这种分页功能,感觉不管查什么都是把所有的结果都捞出来。比如我们只要User表的前10条数据,那么做法是先查询出所有的User数据,再从结果集中取出前10条数据。有人可能会担心,如果数据库中数据非常多,那每次都这么查不会影响性能吗?其实大可放心,由于Realm都是延迟加载的,只有当属性被访问时,才能够读取相应的数据。不像通常数据库,查询后,查询结果是从数据库拷贝一份出来放在内存中的。而Realm的查询结果应该说是数据库数据的引用,就算你查出来,如果不用也不会占用什么内存。

10、支持断言查询(Predicate),这样可以通过条件查询特定数据同时可以使用链式查询数据。

//查询花费超过10元的消费记录(使用断言字符串查询)
consumeItems = realm.objects(ConsumeItem.self).filter("cost > 10")
 
//查询花费超过10元的购物记录(使用 NSPredicate 查询)
let predicate = NSPredicate(format: "type.name = '购物' AND cost > 10")
consumeItems = realm.objects(ConsumeItem.self).filter(predicate)
 
//使用链式查询
consumeItems = realm.objects(ConsumeItem.self).filter("cost > 10").filter("type.name = '购物'")

支持的断言:比较操作数(comparison operand)可以是属性名称或者某个常量,但至少有一个操作数必须是属性名称;
比较操作符 ==、<=、<、>=、>、!=, 以及 BETWEEN 支持 int、long、long long、float、double 以及 NSDate 属性类型的比较,比如说 age == 45;
相等比较 ==以及!=,比如说Results<Employee>().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<Company>().filter("ceo == nil")。注意到这只适用于有关系的对象,这里 ceo 是 Company 模型的一个属性。
ANY 比较,比如说 ANY student.age < 21
注意,虽然我们不支持复合表达式类型(aggregate expression type),但是我们支持对对象的值使用 BETWEEN 操作符类型。比如说,Results<Person>.filter("age BETWEEN %@", [42, 43]])。

11、查询结果的排序

//查询花费超过10元的消费记录,并按升序排列
consumeItems = realm.objects(ConsumeItem.self).filter("cost > 10").sorted(byProperty: "cost")

12、使用List实现一对多关系

List 中可以包含简单类型的 Object,表面上和可变的 Array 非常类似。注意:List 只能够包含 Object 类型,不能包含诸如String之类的基础类型。 如果打算给我们的 Person 数据模型添加一个“dogs”属性,以便能够和多个“dogs”建立关系,也就是表明一个 Person 可以有多个 Dog,那么我们可以声明一个List类型的属性:

class Person: Object {
    ... // 其余的属性声明
    let dogs = List<Dog>()
}
 
// 这里我们就可以使用已存在的狗狗对象来完成初始化
let aPerson = Person(value: ["李四", 30, [aDog, anotherDog]])
 
// 还可以使用多重嵌套
let aPerson = Person(value: ["李四", 30, [["小黑", 5], ["旺财", 6]]])

可以和之前一样,对 List 属性进行访问和赋值:

let someDogs = realm.objects(Dog.self).filter("name contains '小白'")
ZhangSan.dogs.append(objectsIn: someDogs)
ZhangSan.dogs.append(dahuang)

反向关系(Inverse Relationship)通过反向关系(也被称为反向链接(backlink)),您可以通过一个特定的属性获取和给定对象有关系的所有对象。 Realm 提供了“链接对象 (linking objects)” 属性来表示这些反向关系。借助链接对象属性,您可以通过指定的属性来获取所有链接到指定对象的对象。
例如,一个 Dog 对象可以拥有一个名为 owners 的链接对象属性,这个属性中包含了某些 Person 对象,而这些 Person 对象在其 dogs 属性中包含了这一个确定的 Dog 对象。您可以将 owners 属性设置为 LinkingObjects 类型,然后指定其关系,说明其当中包含了 Person 对象。

class Dog: Object {
   dynamic var name = ""
   dynamic var age = 0
    
   // Realm 并不会存储这个属性,因为这个属性只定义了 getter
   // 定义“owners”,和 Person.dogs 建立反向关系
   let owners = LinkingObjects(fromType: Person.self, property: "dogs")
}

13、添加主键(Primary Keys) 重写 Object.primaryKey() 可以设置模型的主键。声明主键之后,对象将被允许查询,更新速度更加高效,并且要求每个对象保持唯一性。一旦带有主键的对象被添加到 Realm 之后,该对象的主键将不可修改。

class Person: Object {
  dynamic var id = 0
  dynamic var name = ""
 
  override static func primaryKey() -> String? {
    return "id"
  }
}

14、添加索引属性(Indexed Properties)重写 Object.indexedProperties() 方法可以为数据模型中需要添加索引的属性建立索引:

class Book: Object {
  dynamic var price = 0
  dynamic var title = ""
 
  override static func indexedProperties() -> [String] {
    return ["title"]
  }
}

15、设置忽略属性(Ignored Properties)重写 Object.ignoredProperties() 可以防止 Realm 存储数据模型的某个属性。Realm 将不会干涉这些属性的常规操作,它们将由成员变量(var)提供支持,并且您能够轻易重写它们的 setter 和 getter。

class Person: Object {
  dynamic var tmpID = 0
  var name: String { // 计算属性将被自动忽略
    return "\(firstName) \(lastName)"
  }
  dynamic var firstName = ""
  dynamic var lastName = ""
 
  override static func ignoredProperties() -> [String] {
    return ["tmpID"]
  }
}

16、修改更新数据

(1)直接更新内容

// 在一个事务中更新对象
try! realm.write {
  consumeItem.name = "去北京旅行"
}

(2)通过主键更新如果您的数据模型中设置了主键的话,那么您可以使用 Realm().add(_:update:) 来更新对象(当对象不存在时也会自动插入新的对象。)

/****** 方式1 ***/
// 创建一个带有主键的“书籍”对象,作为事先存储的书籍
let cheeseBook = Book()
cheeseBook.title = "奶酪食谱"
cheeseBook.price = 9000
cheeseBook.id = 1
 
// 通过 id = 1 更新该书籍
try! realm.write {
    realm.add(cheeseBook, update: true)
}
 
/****** 方式2 ***/
// 假设带有主键值 `1` 的“书籍”对象已经存在
try! realm.write {
    realm.create(Book.self, value: ["id": 1, "price": 22], update: true)
    // 这本书的`title`属性不会被改变
}

(3)键值编码
这个是在运行时才能决定哪个属性需要更新的时候,这个对于大量更新的对象极为有用。

let persons = realm.objects(Person.self)
try! realm.write {
    // 更新第一个
    persons.first?.setValue(true, forKeyPath: "isFirst")
    // 将每个人的 planet 属性设置为“地球”
    persons.setValue("地球", forKeyPath: "planet")
}

17、删除数据

let cheeseBook = ... // 存储在 Realm 中的 Book 对象
 
// 在事务中删除一个对象
try! realm.write {
  realm.delete(cheeseBook)
}

也能够删除数据库中的所有数据

// 从 Realm 中删除所有数据
try! realm.write {
  realm.deleteAll()
}

18、Realm数据库配置

(1)修改默认的的数据库
通过调用 Realm() 来初始化以及访问我们的 realm 变量。其指向的是应用的 Documents 文件夹下的一个名为“default.realm”的文件。
通过对默认配置进行更改,我们可以使用不同的数据库。比如给每个用户帐号创建一个特有的 Realm 文件,通过切换配置,就可以直接使用默认的 Realm 数据库来直接访问各自数据库:

func setDefaultRealmForUser(username: String) {
   var config = Realm.Configuration()
    
   // 使用默认的目录,但是使用用户名来替换默认的文件名
   config.fileURL = config.fileURL!.deletingLastPathComponent()
       .appendingPathComponent("\(username).realm")
    
   // 将这个配置应用到默认的 Realm 数据库当中
   Realm.Configuration.defaultConfiguration = config
}

(2)打包进项目里的数据库的使用如果需要将应用的某些数据(比如配置信息,初始化信息等)打包到一个 Realm 文件中,作为主要 Realm 数据库的扩展,操作如下:

let config = Realm.Configuration(
    // 获取需要打包文件的 URL 路径
    fileURL: Bundle.main.url(forResource: "MyBundledData", withExtension: "realm"),
    // 以只读模式打开文件,因为应用数据包并不可写
    readOnly: true)
 
// 通过配置打开 Realm 数据库
let realm = try! Realm(configuration: config)
 
// 通过配置打开 Realm 数据库
let results = realm.objects(Dog.self).filter("age > 5")

(3)内存数据库内存数据库在每次程序运行期间都不会保存数据。但是,这不会妨碍到 Realm 的其他功能,包括查询、关系以及线程安全。 假如您需要灵活的数据读写但又不想储存数据的话,那么内存数据库对您来说一定是一个不错的选择。

let realm = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "MyInMemoryRealm"))

19、加密数据库

(1)加密后的 Realm文件不能跨平台使用(因为 NSFileProtection 只有 iOS 才可以使用)
(2)Realm 文件不能在没有密码保护的 iOS 设备中进行加密。为了避免这些问题(或者您想构建一个 OS X 的应用),可以使用 Realm 提供的加密方法。
(3)加密过的 Realm 只会带来很少的额外资源占用(通常最多只会比平常慢10%)。

/*****   在创建 Realm 数据库时采用64位的密钥对数据库文件进行 AES-256+SHA2 加密   ****/
// 产生随机密钥
var key = Data(count: 64)
_ = key.withUnsafeMutableBytes { bytes in
    SecRandomCopyBytes(kSecRandomDefault, 64, bytes)
}
         
// 打开加密文件
let config = Realm.Configuration(encryptionKey: key)
let realm:Realm
do {
    realm = try Realm(configuration: config)
} catch let error as NSError {
    // 如果密钥错误,`error` 会提示数据库不可访问
    fatalError("Error opening realm: \(error)")
}
 
// 和往常一样使用 Realm 即可
let dogs = realm.objects(Book.self).filter("name contains 'Fido'")

20、数据迁移(Migration)

(1)为何要迁移比如原来有如下 Person 模型:

class Person: Object {
    dynamic var firstName = ""
    dynamic var lastName = ""
    dynamic var age = 0
}

假如我们想要更新数据模型,给它添加一个 fullname 属性, 而不是将“姓”和“名”分离开来。

class Person: Object {
   dynamic var fullName = ""
   dynamic var age = 0
}

在这个时候如果您在数据模型更新之前就已经保存了数据的话,那么 Realm 就会注意到代码和硬盘上数据不匹配。 每当这时,您必须进行数据迁移,否则当您试图打开这个文件的话 Realm 就会抛出错误。 (2)如何进行数据迁移假设我们想要把上面所声明 Person 数据模型进行迁移。如下所示是最简单的数据迁移的必需流程:

// 在(application:didFinishLaunchingWithOptions:)中进行配置
 
let config = Realm.Configuration(
  // 设置新的架构版本。这个版本号必须高于之前所用的版本号
  // (如果您之前从未设置过架构版本,那么这个版本号设置为 0)
  schemaVersion: 1,
 
  // 设置闭包,这个闭包将会在打开低于上面所设置版本号的 Realm 数据库的时候被自动调用
  migrationBlock: { migration, oldSchemaVersion in
    // 目前我们还未进行数据迁移,因此 oldSchemaVersion == 0
    if (oldSchemaVersion < 1) {
      // 什么都不要做!Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构
    }
  })
 
// 告诉 Realm 为默认的 Realm 数据库使用这个新的配置对象
Realm.Configuration.defaultConfiguration = config
 
// 现在我们已经告诉了 Realm 如何处理架构的变化,打开文件之后将会自动执行迁移
let realm = try! Realm()

虽然这个迁移操作是最精简的了,但是我们需要让这个闭包能够自行计算新的属性(这里指的是 fullName),这样才有意义。 在迁移闭包中,我们能够调用Migration().enumerateObjects(::) 来枚举特定类型的每个 Object 对象,然后执行必要的迁移逻辑。注意,对枚举中每个已存在的 Object 实例来说,应该是通过访问 oldObject 对象进行访问,而更新之后的实例应该通过 newObject 进行访问:

// 在 application(application:didFinishLaunchingWithOptions:) 中进行配置
 
Realm.Configuration.defaultConfiguration = Realm.Configuration(
  schemaVersion: 1,
  migrationBlock: { migration, oldSchemaVersion in
    if (oldSchemaVersion < 1) {
      // enumerateObjects(ofType:_:) 方法遍历了存储在 Realm 文件中的每一个“Person”对象
      migration.enumerateObjects(ofType: Person.className()) { oldObject, newObject in
        // 将名字进行合并,存放在 fullName 域中
        let firstName = oldObject!["firstName"] as! String
        let lastName = oldObject!["lastName"] as! String
        newObject!["fullName"] = "\(firstName) \(lastName)"
      }
    }
  })

21、使用带有 REST API 功能的 Realm 数据库示例我们将从 豆瓣FM的API 那里获取一组 JSON 格式的频道数据,然后将它以 Realm Objects 的形式储存到默认的 Realm 数据库里。

(1)json数据格式如下:

{
  "channels" : [
    {
      "channel_id" : 0,
      "abbr_en" : "My",
      "name_en" : "Personal Radio",
      "seq_id" : 0,
      "name" : "私人兆赫"
    },
    {
      "channel_id" : "1",
      "abbr_en" : "",
      "seq_id" : 0,
      "name" : "华语",
      "name_en" : ""
    },
    {
      "channel_id" : "2",
      "abbr_en" : "",
      "seq_id" : 1,
      "name" : "欧美",
      "name_en" : ""
    }  ]
}

(2)我们将直接把 Dictionary 插入到 Realm 中,然后让 Realm 自行快速地将其映射到 Object 上。(从 iOS9 起,新特性要求App访问网络请求,要采用 HTTPS 协议。直接请求HTTP数据会报错,解决办法可以参照的我另一篇文章:Swift - 网络请求报App Transport Security has blocked a cleartext错)为了确保示例能够成功,我们需要一个所有属性完全匹配 JSON 键结构的 Object 结构体。如果 JSON 的键结构不匹配 Object 结构体属性结构的话,那么就会在插入时被忽略。

  fileprivate func setJsonToRealm (){
        
        // 调用API
        let url = URL(string: "https://www.douban.com/j/app/radio/channels")!
        let resopne = try! Data(contentsOf: url)
        
        let json = try! JSONSerialization.jsonObject(with: resopne, options: .allowFragments) as! [String: Any]
        let channels = json["channels"] as! [[String: Any]]
        
        try! realm.write {
            for channel in channels {
                if channel["seq_id"] as! Int == 0 {continue}//第一个频道数据有问题,丢弃三
                realm.create(DoubanChannel.self, value: channel, update: true)
                
            }
        }
        
        
    }

(3)可以看到数据已经成功插入到库中了

22、当前版本的限制Realm 致力于平衡数据库读取的灵活性和性能。为了实现这个目标,在 Realm 中所存储的信息的各个方面都有基本的限制。例如:

(1)类名称的长度最大只能存储 57 个 UTF8 字符。
(2)属性名称的长度最大只能支持 63 个 UTF8 字符。
(3)NSData 以及 String 属性不能保存超过 16 MB 大小的数据。如果要存储大量的数据,可通过将其分解为16MB 大小的块,或者直接存储在文件系统中,然后将文件路径存储在 Realm 中。如果您的应用试图存储一个大于 16MB 的单一属性,系统将在运行时抛出异常。
(4)对字符串进行排序以及不区分大小写查询只支持“基础拉丁字符集”、“拉丁字符补充集”、“拉丁文扩展字符集 A” 以及”拉丁文扩展字符集 B“(UTF-8 的范围在 0~591 之间)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容