【问题标题】:Why does Realm use try! in Swift?为什么 Realm 使用 try!在斯威夫特?
【发布时间】:2016-05-24 10:16:26
【问题描述】:

为什么 Realm 如此频繁地使用try!?似乎如果您确定您的呼叫不会失败,那么您不应该将其设计为 throw - 不是吗?

这是一个示例,来自 realm.io 上的 Swift 页面:

// Get the default Realm
let realm = try! Realm()

// Persist your data easily
try! realm.write {
  realm.add(myDog)
}

对我来说,这意味着它们永远不会失败,那么为什么要让构造函数或 write() 抛出?

【问题讨论】:

    标签: ios swift error-handling realm


    【解决方案1】:

    如果您指的是 Realm Swift Docs 中的示例,我怀疑为简洁起见,try! 被大量使用。为用户提供了对核心概念的快速而粗略的概述,而无需过多的脑力消耗。

    您可能在使用 Realm 的过程中遇到错误。稍后您会在文档中的 Realms > Error Handling 部分注意到一个 do-catch 示例。

    do {
      let realm = try Realm()
    } catch let error as NSError {
      // handle error
    }
    

    对我来说,这意味着文档中的代码示例不一定是生产质量的,我们鼓励用户使用 Swift 的相关错误处理功能。

    【讨论】:

    • 一开始的警告会帮助新手。
    • 喜欢简洁而不是安全代码实际上是一个好习惯吗?我认为不会。
    【解决方案2】:

    来自写入部分的 Realm Swift 2.1.0 指南:

    因为写入事务可能会像任何其他磁盘一样失败 IO 操作,Realm.write() 和 Realm.commitWrite() 都标记为 throws 这样您就可以处理故障并从故障中恢复,例如用完 磁盘空间。没有其他可恢复的错误。 为简洁起见,我们的 代码示例不处理这些错误,但你当然应该 您的生产应用程序

    来源:https://realm.io/docs/swift/latest/#writes

    【讨论】:

      【解决方案3】:

      我处理这个问题的方法是创建一个 DatabaseManager 类,它处理领域抛出错误的不太可能的事件:

      public class DatabaseManager {
      
          static var realm: Realm {
              get {
                  do {
                      let realm = try Realm()
                      return realm
                  }
                  catch {
                      NSLog("Could not access database: ", error)
                  }
                  return self.realm
              }
          }
      
          public static func write(realm: Realm, writeClosure: () -> ()) {
              do {
                  try realm.write {
                      writeClosure()
                  }
              } catch {
                  NSLog("Could not write to database: ", error)
              }
          }
      }
      

      多亏了这个解决方案,每当我想从领域读取或写入数据库时​​,代码看起来都更干净了 :)

      DatabaseManager.write(realm: realm) {
          let queryResult = self.realm.objects(Cookies.self).filter("cookieId == %@", cookieId)
          let cookie = queryResult.first
          cookie?.expirationDate = expirationDate as NSDate?
      }
      

      【讨论】:

      • 谢谢 da-na,它看起来很干净。但是我不明白如果 Realm() 失败会发生什么,在这种情况下返回 self.realm 是什么?谢谢!!
      • 不幸的是,它会进入一个无限循环,并且应用程序会冻结。所以这里肯定有改进的空间。
      【解决方案4】:

      既然我们可以创建 Realm 的扩展,为什么还要创建一个带有静态函数的类?

      extension Realm {
          static func safeInit() -> Realm? {
              do {
                  let realm = try Realm()
                  return realm
              }
              catch {
                  // LOG ERROR
              }
              return nil
          }
      
          func safeWrite(_ block: () -> ()) {
              do {
                  // Async safety, to prevent "Realm already in a write transaction" Exceptions
                  if !isInWriteTransaction {
                      try write(block)
                  }
              } catch {
                  // LOG ERROR
              }
          }
      }
      

      使用示例

      旧的不安全代码:

      let realm = try! Realm()
      try! realm.write {
          // Your write transaction body
      }
      

      使用此扩展进行安全重构:

      guard let realm = Realm.safeInit() else {
          // Track Error
          return 
      }
      realm.safeWrite {
         // Your write transaction body as before
      }
      

      【讨论】:

      • @james 我一直在测试这个解决方案,guard let realm = Realm.safeInit() else { return } // 然后你的写事务体:realm.safeWrite { ... }
      • @zb1995 我已经改变了你的方法,使用了 guard let :)
      • 太好了,我刚刚提出了另一个编辑,它使 safeWrite 方法对于“领域已经在写入事务中”异常是异步安全的(例如,如果领域已满):github.com/realm/realm-cocoa/issues/4511#issuecomment-270962198
      【解决方案5】:

      来自 Realm 文档:

      到目前为止,您可能已经注意到,我们已经通过调用 Realm() 初始化了对领域变量的访问。该方法返回一个 Realm 对象,该对象映射到应用程序的 Documents 文件夹 (iOS) 或 Application Support 文件夹 (OS X) 下名为“default.realm”的文件。

      每当您与文件系统交互时,您都可能会遇到权限问题或磁盘空间不足等错误。成功是不确定的。

      因此,如果由于任何原因 Realm 无法创建或写入领域文件,那么您引用的这些方法确实会引发异常。

      【讨论】:

        【解决方案6】:

        我为简单的初始化调用创建了这个

        import RealmSwift
        
        // MARK: - RealmDB
        
        /// RealmDB import realm in foundation, and add is format for refactoring catch
        public class RealmDB {
        
            /// Realm
            public static var realm: Realm? {
                do {
                    return try Realm()
                } catch let error {
                    NotificationCenter.default.post(name: .logError, object: "Could not access database: \(error)")
                    return nil
                }
            }
        
            /// Write in Realm
            ///
            /// - Parameter writeClosure: Write Closure
            public static func write(writeClosure: @escaping (_ realm: Realm) -> ()) {
                do {
                    try self.realm?.write {
                        // self.realm has so can `!`
                        writeClosure(self.realm!)
                    }
                } catch let error {
                    NotificationCenter.default.post(name: .logError, object: "Could not write database: \(error)")
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2016-09-19
          • 2017-07-22
          • 2017-03-24
          • 1970-01-01
          • 2016-02-09
          • 1970-01-01
          • 1970-01-01
          • 2018-02-13
          • 1970-01-01
          相关资源
          最近更新 更多