【问题标题】:Best practice for storing temporary data in swift快速存储临时数据的最佳实践
【发布时间】:2018-01-21 22:15:42
【问题描述】:

我开发了一个应用程序,它从服务器获取大量 JSON 数据并显示在不同的 VC 上。我所做的是获取数据并将数据设置为静态变量,所以我可以访问我所有应用中的数据。

现在我注意到我可以使用 CoreDataUserDefaults 代替 Static variables

我的问题是“此类应用的最佳做法是什么?究竟是什么原因?”

注意:该应用无需离线工作并保存数据以在离线模式下显示。

【问题讨论】:

  • 如果不需要离线模式,则不需要数据库。您可以使用 userDefaults 来保存不完整的 json 结果的基本信息,因为它不安全,并且根本不应该保存密码或令牌。最好为此使用 keyChain。更好地为您的服务器数据创建模型类并相应地工作。而且您真的想使用带有 coreData 或 SqlCipher 的数据库。
  • 您是指 JSON 数据的 keyChain 吗? userDefaults 不安全?好吧,我已经在 userDefaults 中保存了令牌
  • 无需在任何地方保存完整的 json 数据,为 json 数据创建模型类并在您的代码中访问。您可以将 json 中的基本信息保存到来自令牌或任何安全凭证的用户默认值。如果您想保存令牌,并且其他安全凭证使用 Apple keyChain 来提供加密,那就更好了。
  • 正确。那么你能给一个“json数据的模型类”示例的链接吗?
  • 你可以谷歌 MVC 看看它是如何工作的。

标签: ios swift core-data swift3 static


【解决方案1】:

您不应该将数据存储到 UserDefaults,用户默认值只是一个基于键值的文件,主要用于存储有关用户的一些数据,例如:

doNotShowAdds : true
isUserLogedIn :true.

您也不应该使用钥匙串,因为钥匙串是加密的容器,您可以在其中存储有关用户的评论数据,例如:密码。

你不需要使用数据库

我的观点是你不应该使用全局变量,主要是我尝试让 viewController 数据特定于自己,不应该从项目中的任何地方访问数据。 When to use global variables in Swift

主要是我为特定的视图控制器制定服务协议。 无论如何,每个控制器都应该有自己的数据部分,例如

class MyViewControllerOne {
    private var data :Array<DataExampleOne>;
}

class MyViewControllerTwo {
    private var data :Array<DataExampleTwo>;
}

一旦您从 API 加载数据,数据就会一直存在,直到您关闭应用程序。

您总是可以创建另一个包含这些数据的类,并且可能是用于更简洁方法的谓词,例如:

protocol MyViewControllerOneService {
    func search() -> Array<DataExampleOne>;
}

class MyViewControllerOneServiceImpl :MyViewControllerService {
public var data :Array<DataExampleOne>;

public func search(searchString :String) -> Array<DataExampleOne>{
    let predicate = NSPredicate() ...
    let filteredArray = self.data.filter { evaluate...}
    return filteredArray;
    }
}

我使用类似的方法,而不是将数据保存在我的服务类(业务逻辑)中,而是获得了用于从数据库获取数据的存储库。由于您没有任何数据库,因此这种方法是可以的。

然后代替

class MyViewControllerOne {
    private var data :Array<DataExampleOne>;
}

你可以使用

class MyViewController {
    private var myViewControllerService :MyViewControllerOneServiceImpl;
}

希望对你有帮助, 最好的问候!

【讨论】:

    【解决方案2】:

    讨论:

    “此类应用的最佳做法是什么?为什么?”是一个很好的问题。如果您描述您正在开发的应用程序类型会有所帮助。

    既然您似乎只在应用程序运行时需要/想要数据,那么我想最好的方法是将其保存在您已经在做的一些单例或静态变量的内存中。

    但是,如果您不不想在应用重新启动后存储您的数据,那么您可能希望您的数据在一段时间后过期,因为应用可能会打开很长时间。 p>

    那么问题是您希望如何在运行时接收数据。假设您已经缓存了数据,并且您仍然希望刷新数据。然后你可以设计你的数据管理,这样你获取项目的关闭可能会被调用多次。假设有这样的事情:

        func fetchUsers(callback: ((_ users: [Any]?, _ isFromCache: Bool) -> Void)) {
            if let cachedUsers = MyCache.fetch(.users) {
                callback(cachedUsers, true)
            }
    
            APIManager.fetch(.users, { users, error in
                if let users = users {
                    MyCache.insert(.users, items: users)
                }
                callback(users, false)
            })
        }
    

    假设您想在某个表格视图中显示这些用户,您可以执行以下操作:

        func refreshData() {
            self.startActivityIndicator() // Start some minor activity indicator
            fetchUsers { (users, isFromCache) in
                self.users = users // Assign users whatever they are
                if users == nil {
                    // Show error here
                }
    
                if  isFromCache == false {
                    self.stopActivityIndicator() // Only stop activity when fresh data were received so user know he may expect the UI to change
                }
    
                tableView.reloadData()
            }
        }
    

    这将设计数据获取的用户界面部分,但与数据的存储位置和方式无关。例如MyCache 应该仍然选择缓存响应仍然有效或过时的天气。 MyCache 决定将数据存储到某个数据库中还是仅存储在内存中。

    一些存储过程:

    为了保持之前的 sn-ps 中的设计,您可能有一个静态类 MyCache,它具有存储类型的字符串枚举。然后它有一个静态字典[String: Any],其中包含缓存数据,因此 fetch 方法看起来像

    func fetch(type: FetchType) {
        return cacheDictionary[type]
    }
    

    那么插入非常相似。

    要添加日期(或其中的任何其他数据),您可以将其定义为 [String: [String: Any]] 并使用静态键,因此您的数据将是 cacheDictionary[type]["date"] as? Date 和数据为 cacheDictionary[type]["data"]

    然后您可以扩展您的缓存以使用用户默认值,例如:

    func fetch(type: FetchType) {
        if let inMemory = cacheDictionary[type] {
            return inMemory
        } else if let inDefaults = UserDefaults.standard.object(forKey: type) {
            cacheDictionary[type] = inDefaults // Keep it in memory for net time
            return inDefaults
        } else {
            return nil
        }    
    }
    

    这是用于存储直接 JSON 数据或对象。同样可以应用于将数据存储(使用JSONSerializer 对其进行序列化)到文件中。您可以在临时目录或库中创建一个基本路径,然后使用type 的文件名。系统可能会清除您可能想要的临时目录,并且库目录将保持不变。

    然后是数据库:

    数据库是另一回事。将数据库应用到与上述相同的过程中是没有意义的,因为这太过分了,而且您根本没有使用数据库的任何好处。

    对于数据库,您将为每个响应创建模型并将数据插入数据库,然后通知您的 UI 控制器进行更新。这意味着回调不是最佳实践,而是委托。例如,您将拥有DatabaseManager 并且在视图中确实出现了您会调用DatabaseManager.shared.delegate = self 然后Users.fetchNewUsers() 并等待委托触发。然后在委托上,您将从数据库中获取对象或创建获取结果控制器...

    可能还有很多其他方法,但我希望这能让您了解如何存储数据,何时使用。

    【讨论】:

    • 亲爱的投票者,请解释您采取行动的原因。 “由于您似乎只在应用程序运行时需要/想要数据,所以我想最好的方法是将它保存在您已经在做的一些单例或静态变量的内存中。”是这个问题的最佳答案。
    【解决方案3】:

    基本上我是三个选项:

    全局变量: 我需要定义全局结构:

    struct GlobalUser {
        static var name : String
    
    }
    

    现在我可以从项目中的任何位置简单地调用全局变量:

    GlobalUser.name = "Nazmul Hasan"
    

    用户默认值

    它用于数据存储,但不是敏感数据:

          //key
           struct defaultsKeys {
            static var name = "name"   
           }
    
         //Setting 
            let defaults = UserDefaults.standard
            defaults.set("Nazmul", forKey: defaultsKeys.name)
    
        // Getting
        let defaults = UserDefaults.standard
        if let userName = defaults.string(forKey: defaultsKeys.name) {
            print(userName) // print what you store 
        }
    

    钥匙链 它用于数据存储,但属于敏感数据,如用户名、密码或令牌。

    Save and retrieve value via KeyChain swift

    但我个人还是推荐流这个由苹果发布的博客

    Apple iOS Data Storage Guidelines

    更感兴趣的阅读这个问题线程:

    How to save local data in a Swift app?

    【讨论】:

      【解决方案4】:

      您可以创建数据模型类来存储来自 JSON 响应的临时数据。

      数据模型类示例:-

      class RootClass : NSObject, NSCoding{
      
          var array : [Int]!
          var booleanField : Bool!
          var custArray : [CustArray]!
          var nullField : AnyObject!
          var number : Int!
          var object : Object!
          var string : String!
      
      
          /**
           * Instantiate the instance using the passed dictionary values to set the properties values
           */
          init(fromDictionary dictionary: [String:Any]){
              booleanField = dictionary["Boolean"] as? Bool
              nullField = dictionary["Null"] as? AnyObject
              number = dictionary["Number"] as? Int
              string = dictionary["String"] as? String
              if let objectData = dictionary["Object"] as? [String:Any]{
                  object = Object(fromDictionary: objectData)
              }
              custArray = [CustArray]()
              if let custArrayArray = dictionary["CustArray"] as? [[String:Any]]{
                  for dic in custArrayArray{
                      let value = CustArray(fromDictionary: dic)
                      custArray.append(value)
                  }
              }
          }
      
          /**
           * Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
           */
          func toDictionary() -> [String:Any]
          {
              var dictionary = [String:Any]()
              if booleanField != nil{
                  dictionary["Boolean"] = booleanField
              }
              if nullField != nil{
                  dictionary["Null"] = nullField
              }
              if number != nil{
                  dictionary["Number"] = number
              }
              if string != nil{
                  dictionary["String"] = string
              }
              if object != nil{
                  dictionary["object"] = object.toDictionary()
              }
              if custArray != nil{
                  var dictionaryElements = [[String:Any]]()
                  for custArrayElement in custArray {
                      dictionaryElements.append(custArrayElement.toDictionary())
                  }
                  dictionary["custArray"] = dictionaryElements
              }
              return dictionary
          }
      
          /**
           * NSCoding required initializer.
           * Fills the data from the passed decoder
           */
          @objc required init(coder aDecoder: NSCoder)
          {
              array = aDecoder.decodeObject(forKey: "Array") as? [Int]
              booleanField = aDecoder.decodeObject(forKey: "Boolean") as? Bool
              custArray = aDecoder.decodeObject(forKey: "CustArray") as? [CustArray]
              nullField = aDecoder.decodeObject(forKey: "Null") as? AnyObject
              number = aDecoder.decodeObject(forKey: "Number") as? Int
              object = aDecoder.decodeObject(forKey: "Object") as? Object
              string = aDecoder.decodeObject(forKey: "String") as? String
          }
      
          /**
           * NSCoding required method.
           * Encodes mode properties into the decoder
           */
          @objc func encode(with aCoder: NSCoder)
          {
              if array != nil{
                  aCoder.encode(array, forKey: "Array")
              }
              if booleanField != nil{
                  aCoder.encode(booleanField, forKey: "Boolean")
              }
              if custArray != nil{
                  aCoder.encode(custArray, forKey: "CustArray")
              }
              if nullField != nil{
                  aCoder.encode(nullField, forKey: "Null")
              }
              if number != nil{
                  aCoder.encode(number, forKey: "Number")
              }
              if object != nil{
                  aCoder.encode(object, forKey: "Object")
              }
              if string != nil{
                  aCoder.encode(string, forKey: "String")
              }
          }
      }
      

      创建数据模型类的简单方法:- jsoncafe.com

      【讨论】:

        【解决方案5】:

        对于像 key : value 这样的“小”数据,最好使用 UserDefaults。如果您有更多存储空间,请使用 CoreDara,因为这基本上是一个 SQLite 数据库。

        【讨论】:

        • 整个数据存储在服务器上的数据库中,由于我不需要离线支持,所以我认为不需要 coreData。对吧?
        • 离线支持只是 coredata 的一个论据。如果你想预加载一堆数据集,CoreData 也是理想的选择,在缺少连接的情况下让一些数据离线,它可能会减少与数据库的连接量,从而减少所需的电池电量。
        猜你喜欢
        • 1970-01-01
        • 2010-12-30
        • 2022-06-29
        • 2015-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多