【问题标题】:Storing an array of strings using Realm's RLMArray使用 Realm 的 RLMArray 存储字符串数组
【发布时间】:2015-07-30 13:04:12
【问题描述】:

有谁知道如何使用 Realm 存储字符串数组?我正在尝试将以下响应正确映射到领域:

"zoneInfo": {
    "tariffInfoLines": [
        "In this city you pay per minute."
    ]
}

我们有一个 zoneInfo 对象,其中包含一个关税信息线数组。此关税信息线数组包含字符串。在 Realm 中有两种不同的变量类型用于存储数据。第一个是RLMObject,它允许您使用标准的 NSString、int、long 等。

第二种是RLMArray,用于数组(因为不支持NSArray)。您必须为数组指定一个类型,该类型必须是 RLMObject 的子类。到目前为止,我们已经通过使用 ABCRealmString 对象解决了这个问题,如下所示:

@property RLMArray<ABCRealmString> *tariffInfoLines;

ABCRealmString 包含一个 NSString 属性(它基本上是一个包装器):

@property NSString *value;

然而,这意味着当 Realm 尝试映射响应以保存数据时,它正在为键“值”(属性的名称)寻找一个值。它似乎期望得到类似于以下内容的响应:

"zoneInfo": {
    "tariffInfoLines": [
        {
            "value": "In this city you pay per minute."
        },
    ]
}

在项目中,我们让它适用于以下结构:

"userOptions": [
    {
        "wantsEmailNotifications": true,
        "wantsPushNotifications": false
    },
]

这有一个数组,里面的对象有明确的键值对,Realm 可以映射到。 zoneInfo 结构似乎是唯一一个我们有一个数组的地方,其中包含一组值,而它们不在对象内或没有任何键。

如果有人能对此有所了解,是否可以使用 Realm,或者是否需要更改 API 以匹配 Realm 可以映射的结构。

【问题讨论】:

    标签: ios objective-c realm


    【解决方案1】:

    来自github issue 响应的交叉发帖:尽管此示例演示了如何在 Realm 模型上存储平面字符串数组,但您可以扩展此模式以存储从整数数组到本机 Swift 枚举的任何内容。基本上任何可以映射到 Realm 中的可表示类型的东西。

    class RealmString: Object {
        dynamic var stringValue = ""
    }
    
    class Person: Object {
        var nicknames: [String] {
            get {
                return _backingNickNames.map { $0.stringValue }
            }
            set {
                _backingNickNames.removeAll()
                _backingNickNames.appendContentsOf(newValue.map({ RealmString(value: [$0]) }))
            }
        }
        let _backingNickNames = List<RealmString>()
    
        override static func ignoredProperties() -> [String] {
            return ["nicknames"]
        }
    }
    
    // Usage...
    
    let realm = try! Realm()
    try! realm.write {
        let person = Person()
        person.nicknames = ["John", "Johnny"]
        realm.add(person)
    }
    
    for person in realm.objects(Person) {
        print("Person's nicknames: \(person.nicknames)")
    }
    
    // Prints:
    // Person's nicknames: ["John", "Johnny"]
    

    【讨论】:

    • 我收到一个错误Cannot assign a value of type [RealmString] to a value of type 'List&lt;RealmString&gt;'。如何将 RealmString 数组转换为 List
    • 我自己现在也有同样的问题。
    • @jpsim 您能否确认或证实其他答案的说法:“RealmString 方法很好,但每次更新值时都会得到一个新的 RealmString,留下一个如果您不清理它们,就会有大量未使用的物体。”。如果正确,如何清理未使用的对象?
    • 最好的方法是如果 Realm 支持级联删除,这正在开发中,但不会很快发布。相反,您可以在应用程序启动时(或其他方便的时间)迭代这些 RealmString 对象并删除没有“链接对象”的对象(例如未使用的对象)。见realm.io/docs/swift/latest/#inverse-relationships
    • 是的,每次更新都会得到新的字符串(因为这里没有主键)。级联删除尚未实现,但您可以添加 parentID 之类的内容或自己创建主键。
    【解决方案2】:

    更新(之前的大部分答案都不再正确):

    您现在可以直接在 RLMArrays 或 Lists 中存储原始类型或其可为空的对应物(更具体地说:布尔、整数和浮点数类型、字符串、日期和数据)。如果要定义此类原始值的列表,则不再需要定义繁琐的单字段包装器对象。相反,您可以只存储原始值本身。

    原始值列表的工作方式与包含对象的列表大致相同,如下面的 Swift 示例所示:

    class Student : Object {
        @objc dynamic var name: String = ""
        let testScores = List<Int>()
    }
    
    // Retrieve a student.
    let realm = try! Realm()
    let bob = realm.objects(Student.self).filter("name = 'Bob'").first!
    
    // Give him a few test scores, and then print his average score.
    try! realm.write {
        bob.testScores.removeAll()
        bob.testScores.append(94)
        bob.testScores.append(89)
        bob.testScores.append(96)
    }
    print("\(bob.testScores.average()!)")   // 93.0
    

    Realm 支持的所有其他语言也支持原始类型列表。

    【讨论】:

      【解决方案3】:

      对于 Swift 3.0,这里是变化(在我的情况下,当我切换到 swift 3.0 时,Xcode 8 编译器没有提供自动修复,所以我很难解决它)。

      _backingNickNames.append(objectsIn: newValue.map { RealmString(value: [$0]) })
      

      【讨论】:

      • 使用当前的 Xcode,只需单击错误的红点即可将旧语法自动更正为新语法。
      【解决方案4】:

      RealmString 方法很好,但每次更新值时都会得到一个新的RealmString,如果不清理它们就会留下大量未使用的对象。

      我建议使用类似的东西:

      fileprivate let separator = "\u{FFFF}"
      
      class Person: Object {
          fileprivate dynamic var _nicknames: String?
          var nicknames: [String] {
              get { return _nicknames?.components(separatedBy: separator) ?? [] }
              set { _nicknames = newValue.isEmpty ? nil : newValue.joined(separator: separator) }
          }
      
          override static func ignoredProperties() -> [String] {
              return ["nicknames"]
          }
      }
      

      【讨论】:

      • 所以你建议用不同的 Unicode 字符替换逗号的 CSV 技术?让我引用this answer 来证明在一般情况下应避免您的建议:“Unicode 联盟最近发布了Corrigendum 9,阐明了非字符的作用,包括 U+FFFF,在 Unicode 字符串中。它指出,虽然非字符是供内部使用的,但它们可以合法在 Unicode 字符串中出现。“但对于特定情况,它可能会起作用。
      【解决方案5】:
      extension String {
          func toStringObject() -> StringObject {
              return StringObject(initValue: self)
          }
      }
      
      extension Sequence where Iterator.Element == String {
          func toStringObjects() -> List<StringObject> {
              let list = List<StringObject>()
              for s in self {
                  list.append(s.toStringObject())
              }
              return list
          }
      }
      
      extension Int {
          func toIntObject() -> IntObject {
              return IntObject(initValue: self)
          }
      }
      
      extension Sequence where Iterator.Element == Int {
          func toIntObjects() -> List<IntObject> {
              let list = List<IntObject>()
              for s in self {
                  list.append(s.toIntObject())
              }
              return list
          }
      }
      

      【讨论】:

      • 你最好解释你的解决方案,而不是仅仅发布一些匿名代码。你应该阅读How do I write a good answer
      • 感谢您的意见!当您的数据具有字符串数组或 int 数组数组时,我尝试了我的解决方案并且它成功了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-26
      • 2017-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多