[IOS]数据永久化

数据存储

本文介绍了在IOS中常见的几种保存数据的方式,以及相关的实现方法(基于swift)。

思维导图:
Subtitle4copy

应用程序沙盒

每个IOS程序有一套自己独立的文件系统,其路径以 / 开始, 这个文件系统成为应用程序沙盒。每个应用程序只能在自己的沙盒内部读写文件,基本不可以去访问外部文件。所有的操作都要进行权限检测。

沙盒是一种安全机制,其核心是对IOS应用的操作进行权限检测。
Subtitle6
Subtitle7
Subtitle8

Subtitle10

为什幺要用沙盒?

  • 防止应用被其他应用恶意修改、访问和删除。
  • 防止其他软件访问你的个人数据。
  • 能够很干净地清楚数据。
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    // In Unix, ~ -> /User/User name/...

    // fetch document file address.
    var paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, false)
    let doucmentDirectory = paths[0] as String
    print(doucmentDirectory)

    var tempDirectory = NSTemporaryDirectory()

    // manager file.
    let fileManager = NSFileManager.defaultManager()
    let DoucementDirectoryWithManager = fileManager.URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask)
    print(DoucementDirectoryWithManager)
  }

NSUserDefaults

提供一个与系统默认设置进行交互的编程接口,用于保存,恢复应用的偏好设置,配置数据等等。
将数据对象存储到“默认系统”的地方,能够持久化。
是一个单例。
适合存储轻量级的存储数据。

可以存储的数据类型

  • NSData
  • NSNumber
  • NSString
  • NSDate
  • NSArray
  • NSDictionary
  • Bool

示例

  @IBOutlet weak var TextField: UITextField!
  // The unique object!
  var Users = NSUserDefaults.standardUserDefaults()
  @IBAction func DataSave(sender: UIButton) {
    let text = TextField.text
    // Just the database, with value-to-key, it can set the data.
    // Each key is unique.
    Users.setObject(text, forKey: "Text")
    // Don't forget to sync!
    Users.synchronize()
  }

  @IBAction func DataLoad(sender: UIButton) {
    // get string value.
    let text = Users.stringForKey("Text")
    TextField.text = text
  }

Settings Bundle

在自己的应用中建立的一组文件,利用它可以告诉设备中的“设置”应用。(特别是在偏好设置)

plist file

Subtitle13

在Settings bundle里面可以给定一些特定类型的控件,并设置相应的键值对。(如果没有给出键值就不会显示。)(右键可以选择显示键值对。)

特别值得注意的是,在Text控件里面可以改为Child Pane。这是一个子视图入口,需要在Settings bundle里面创建另一个plist。直接创建是创建不了的,只能进入文件夹内部进行操作。(如下图)然后在File属性里面给出相应的文件名就可以访问了。
Subtitle12

访问Settings

//
//  ViewController.swift
//  数据持久化
//
//  Created by 颜泽鑫 on 7/7/16.
//  Copyright © 2016 颜泽鑫. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    // loadDefaults()
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
    //    getUserDefaults()
  }
  @IBOutlet weak var TextField: UITextField!
  // The unique object!
  var Users = NSUserDefaults.standardUserDefaults()
  @IBAction func DataSave(sender: UIButton) {
    updateUserDefaults()
  }
  @IBAction func DataLoad(sender: UIButton) {
    getUserDefaults()
  }
  /**
   Load Settings.bundles information and put them into NSUserDefaults.
   */
  func loadDefaults() {
    /// Enter the Settings.bundle.
    let SettingsBubble = NSBundle.mainBundle().pathForResource("Settings", ofType: "bundle")
    if SettingsBubble == nil {
      return
    } else {
      // enter the More plist file.
      // pay attention to `/`.
      // stringByAppendingString : append another stirng.
      // the valid URL gap by `/`.
      // in this file, the data is save as dictionary.
      let root = NSDictionary(contentsOfFile: SettingsBubble!.stringByAppendingString("/More.plist"))
      //  By key, you can get the whole Array,
      let preferences = root?.objectForKey("PreferenceSpecifiers") as! Array<NSDictionary>
      let defaultToRegister = NSMutableDictionary(capacity: root!.count)
      // visit the whole array, and put them together into `defaultToRegister`.
      for preference in preferences {
        let key = preference.objectForKey("Key") as! String?
        if key != nil {
          defaultToRegister.setValue(preference.objectForKey("DefaultValue"), forKey: key!)
        }
      }
      // put the `defaultToRegister` into NSUserDefaults so that they can be load.
      NSUserDefaults.standardUserDefaults().registerDefaults((defaultToRegister as NSDictionary) as! [String : AnyObject])
    }
  }
  func getUserDefaults() {
    // Because of having load info, you can easy get value by its key.
    let Defaults = NSUserDefaults.standardUserDefaults()
    TextField.text = Defaults.objectForKey("name_preferences") as? String
  }

  func updateUserDefaults() {
    let Defaults = NSUserDefaults.standardUserDefaults()
    Defaults.setBool(false, forKey: "enabled_preference")
    // Don't forget to sync.
    Users.synchronize()
  }
}

通用文件存储

Subtitle2

Subtitle3
Subtitle4
Subtitle5

这就类似于在C中做文件操作,只能完全地读入文件内信息,对于类似于Database的信息,仍然需要做特别的转换。(可以使用json文件,目前还没有找到合适的json库。)

示例:

  @IBOutlet weak var TextField: UITextField!
  // The unique object!
  var Users = NSUserDefaults.standardUserDefaults()
  @IBAction func DataSave(sender: UIButton) {
    let text = TextField.text! as NSString
    /**
     *  Write the info into file.
     *  Using error handling, too.
     */
    do {
      try text.writeToFile(getFilePath("data.txt"), atomically: true, encoding: NSUTF8StringEncoding)
    } catch {
      print("Can't save!")
      return
    }
  }

  @IBAction func DataLoad(sender: UIButton) {
    let textFilePath = getFilePath("data.txt")
    //  Judge if the file exists.
    if NSFileManager.defaultManager().fileExistsAtPath(textFilePath) {
      /**
       *  Transfer the file info to NSString is `throws` functions.
       *  So it is neccessary to use error handling method.
       */
      do {
        let text = try NSString(contentsOfFile: textFilePath, encoding: NSUTF8StringEncoding)
        TextField.text = text as String
      } catch {
        print("Can't load!")
      }
    } else {
      print("File don't exist!")
    }
  }
  /**
   In this function, we will get the path of file.

   - parameter filename: The file name you wanna load.

   - returns: path
   */
  func getFilePath(filename : String) -> String {
    let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
    var DocumentPath = path[0] as NSString
    /**
     *  You still need to pay attention to add `/` before file name.
     *  So that you can interval the different component.
     *  @param "/"
     *
     *  @return path
     */
    DocumentPath = DocumentPath.stringByAppendingString("/")
    return DocumentPath.stringByAppendingString(filename)
  }

对象模型归档

用于存储一些在通用文件保存中无法保存的类型,例如图片、视频等等

两个关键概念:

  • 对象归档(Archive): 将对象转换成一种可以写入文件的格式,通常是以一种不可读的方式进行保存。
  • 对象反归档(Unarchive):将数据从文件中读出并自动重建对象。

示例

import Foundation
import UIKit
/// This is the data type which we wanna save.
class Person : NSObject, NSCoding {
  var name = ""
  var logos : UIImage!

  override init() {
     super.init()
  }
  /**
   This method is neccessary which is used to get info
   from the file.
   */
  required init?(coder aDecoder: NSCoder) {
    super.init()
    name = aDecoder.decodeObjectForKey("name") as! String
    logos = aDecoder.decodeObjectForKey("logo") as! UIImage
  }
  /**
   This method is used to input info into the file.
   */
  func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(name, forKey: "name")
    aCoder.encodeObject(logos, forKey: "logo")
  }
}
//
//  ViewController.swift
//  数据持久化
//
//  Created by 颜泽鑫 on 7/7/16.
//  Copyright © 2016 颜泽鑫. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    // loadDefaults()

  }
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
    //    getUserDefaults()
  }

  @IBOutlet weak var TextField: UITextField!
  // The unique object!
  @IBOutlet weak var logo: UIImageView!

  @IBAction func DataSave(sender: UIButton) {
    let text = TextField.text!
    let logos = UIImage(named: "1")
    let textPath = getFilePath("data.txt")
    let person = Person()
    person.name = text
    person.logos = logos
    /// Archive the `person` and then write it into the file.
    let data = NSKeyedArchiver.archivedDataWithRootObject(person)
    do {
      try data.writeToFile(textPath, options: NSDataWritingOptions.AtomicWrite)
    } catch {
      print("Write error!")
    }
  }

  @IBAction func DataLoad(sender: UIButton) {
    let textPath = getFilePath("data.txt")
    /// Unarchive the file to get `Person` info.
    let person = NSKeyedUnarchiver.unarchiveObjectWithFile(textPath) as! Person
    TextField.text = person.name
    logo.image = person.logos
  }
  /**
   In this function, we will get the path of file.

   - parameter filename: The file name you wanna load.

   - returns: path
   */
  func getFilePath(filename : String) -> String {
    let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
    var DocumentPath = path[0] as NSString
    /**
     *  You still need to pay attention to add `/` before file name.
     *  So that you can interval the different component.
     *  @param "/"
     *
     *  @return path
     */
    DocumentPath = DocumentPath.stringByAppendingString("/")
    return DocumentPath.stringByAppendingString(filename)
  }
}

Core Data

Core Data是IOS对关系型数据库的一种封装,类似于编写Database一样保存数据。但是缺点是,相对比较麻烦,除非数据非常大,否则尽量不使用。

//
//  ViewController.swift
//  core
//
//  Created by 颜泽鑫 on 7/8/16.
//  Copyright © 2016 颜泽鑫. All rights reserved.
//

import UIKit
import CoreData
class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    /// Get AppDelegate object.
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    /// Get context which save the info of Company.
    let context = appDelegate.managedObjectContext
    /// This Request can execute all operation like, update, save or read.
    let companyRequest = NSFetchRequest(entityName: "Company")
    do {
      let companyObjects = try context.executeFetchRequest(companyRequest) as! [NSManagedObject]
      for object in companyObjects {
        let name = object.valueForKey("name") as! String
        let age = object.valueForKey("age") as! Int
        let area = object.valueForKey("area") as! String
        print("load Data name : \(name)")
        print("load Data age : \(age)")
        print("load Data area : \(area)")
      }
    } catch {
      print("Fetch error!")
    }
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }
}

AppDelegate里面还保存了很多相关的操作,可以仔细阅读相关注释。

// in AppDelegate
  func applicationWillTerminate(application: UIApplication) {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    // Saves changes in the application's managed object context before the application terminates.
    let name = "stary"
    let age = 20
    let area = "ShenZhen"
    let companyRequest = NSFetchRequest(entityName: "Company")
    do {
      let companyObjects = try managedObjectContext.executeFetchRequest(companyRequest) as! [NSManagedObject]

      var Company : NSManagedObject?
      /**
       *  Judge if there is Company object.
       *  If there isn't one, then you can initialize a new one.
       */
      if companyObjects.count > 0 {
        Company = companyObjects[0]
      } else {
        Company = NSEntityDescription.insertNewObjectForEntityForName("Company", inManagedObjectContext: managedObjectContext) as NSManagedObject
      }
      Company?.setValue(name, forKey: "name")
      Company?.setValue(age, forKey: "age")
      Company?.setValue(area, forKey: "area")
    } catch {
      print("Save error!")
    }
    self.saveContext()
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值