iOS ~ Swift之數(shù)據(jù)存儲(.plist,NSUserDefaults,NSKeyedArchiver)

最近公司項目中使用了本地存儲蕉鸳,所以趁著周六日來整理一下在Swift中的幾種數(shù)據(jù)存儲方式辕近。

一抬虽、plist文件存儲

存儲目錄:Documents

每次在Xcode中新建一個iOS項目后脑题,都會自己產(chǎn)生一個.plist文件许师,里面記錄項目的一些配置信息肉渴。我們也可以自己創(chuàng)建.plist文件來進行數(shù)據(jù)的存儲和讀取公荧。
.plist文件其實就是一個XML格式的文件,支持的數(shù)據(jù)類型有NSDictionary同规、NSArray循狰、Boolean、NSData券勺、NSDate绪钥、NSNumber、NSString這些類型关炼。

在這里簡單說一下關(guān)于應用程序各個文檔所在的目錄:
  • Home目錄 ./

獲取方式:
let homePath = NSHomeDirectory()
打印出來以后程腹,可以復制到Finder->前往 -> 前往文件夾中,查看文檔儒拂。


home.png

正如我們看到的寸潦,在home下有三個文件夾,下面我們分別來看一下這三個目錄社痛。

  • 1.Documents目錄 ./Documents

用戶文檔目錄见转,蘋果建議將程序中建立的或者在程序中瀏覽到的文件數(shù)據(jù)保存到該目錄下,iTunes備份和恢復的時候會包括此目錄
獲取方式:
//方法1
//swift 2
let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0].stringByAppendingString("user")
//swift 3
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0].appending("user")
//方法2
let ducumentPath2 = NSHomeDirectory() + "/Documents"

  • 2.Library目錄 ./Library

這個目錄下有兩個子目錄:Caches 和 Preferences
Library/Preferences目錄蒜哀,包含應用程序的偏好設(shè)置文件斩箫。iOS的Settings(設(shè)置)應用會在該目錄中查找應用的設(shè)置信息。不應該直接創(chuàng)建偏好設(shè)置文件撵儿,而是應該使用NSUserDefaults類來取得和設(shè)置應用程序的偏好乘客。
Library/Caches目錄,主要存放緩存文件淀歇,iTunes不會備份此目錄寨典,此目錄下文件不會再應用退出時刪除。一般存儲體積大房匆、不需要備份的非重要數(shù)據(jù)耸成。
//Library目錄-方法1
let libraryPaths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.LibraryDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let libraryPath = libraryPaths[0] as! String
//Library目錄-方法2
let libraryPath2 = NSHomeDirectory() + "/Library"

  //Cache目錄-方法1
  let cachePaths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true)
  let cachePath = cachePaths[0] as! String
  //Cache目錄-方法2
  let cachePath2 = NSHomeDirectory() + "/Library/Caches"
  • 3.tmp目錄 ./tmp

保存應用運行時所需的臨時數(shù)據(jù),使用完畢后再將相應的文件從該目錄刪除浴鸿。重啟后清空井氢。應用沒有運行時,系統(tǒng)也可能會清除該目錄下的文件岳链。iTunes同步設(shè)備時不會備份該目錄花竞。
//方法1
let tmpDir = NSTemporaryDirectory()
//方法2
let tmpDir2 = NSHomeDirectory() + "/tmp"

說了半天還沒有給大家介紹怎么把數(shù)據(jù)寫入.plist文件中,我們下面以數(shù)組為例,來看一下:
    let array = NSArray(objects: "apple","orange","pear","banana","watermelon")
    let filePath:String = NSHomeDirectory() + "/Documents/fruit.plist"
    array.writeToFile(filePath, atomically: true)

這個時候我們會看到在Documents文件夾下面產(chǎn)生了一個.plist文件约急,打開后會發(fā)現(xiàn)是我們剛剛存入的水果名稱零远。


plist1.png
plist2.png
存入plist后我們來讀取一下fruit.plist中的數(shù)據(jù):
let fruitArr = NSArray(contentsOfFile: NSHomeDirectory() + "/Documents/fruit.plist")

同理,我們還可以存入.plist文件支持的其他類型的數(shù)據(jù)厌蔽。

缺點:用.plist文件只能存儲含有writeToFile:方法的對象牵辣,局限性。

二奴饮、NSUserDefaults

存儲目錄:Library/Preference

用來保存應用程序設(shè)置和屬性纬向、用戶保存的數(shù)據(jù),用戶再次打開程序或開機后這些數(shù)據(jù)仍然存在戴卜。NSUserDefaults可以存儲的數(shù)據(jù)類型包括:NSData逾条、NSString、NSNumber投剥、NSDate师脂、NSArray、 NSDictionary江锨。如果要存儲其他類型,則需要轉(zhuǎn)換為前面的類型,才能用NSUserDefaults存儲危彩。很多iOS應用都支持偏好設(shè)置,比如保存用戶名泳桦、密碼汤徽、字體大小等設(shè)置,iOS提供了一套標準的解決方案來為應用加入偏好設(shè)置功能灸撰,每個應用都有個NSUserDefaults實例谒府,通過它來存取偏好設(shè)置,比如浮毯,保存用戶名完疫、字體大小、是否自動登錄等债蓝。

示例:
  • 1.對原生數(shù)據(jù)類型的存儲和讀取
    var userDefault = NSUserDefaults.standardUserDefaults()

    //AnyObject
    userDefault.setObject("apple", forKey: "Object")
    var objectValue:AnyObject? = userDefault.objectForKey("Object")
    
    //Int類型
    userDefault.setInteger(12345, forKey: "Int")
    var intValue = userDefault.integerForKey("Int")
    
    //Float類型
    userDefault.setFloat(3.2, forKey: "Float")
    var floatValue = userDefault.floatForKey("Float")
    
    //Double類型
    userDefault.setDouble(5.2240, forKey: "Double")
    var doubleValue = userDefault.doubleForKey("Double")
    
    //Bool類型
    userDefault.setBool(true, forKey: "Bool")
    var boolValue = userDefault.boolForKey("Bool")
    
    //NSURL類型
    userDefault.setURL(NSURL(string:"http://baidu.com")!, forKey: "NSURL")
    var urlValue = userDefault.URLForKey("NSURL")
    
    //NSString類型
    userDefault.setObject("apple", forKey: "NSString")
    var nsStringValue = userDefault.objectForKey("NSString") as! NSString
    
    //NSNumber類型
    var number:NSNumber = NSNumber(int:22)
    userDefault.setObject(number, forKey: "NSNumber")
    number = userDefault.objectForKey("NSNumber") as! NSNumber
    
    //NSArray類型
    var array:NSArray = NSArray(array: ["123","456"])
    userDefault.setObject(array, forKey: "NSArray")
    
    //NSDictionaryy類型
    var dictionary:NSDictionary = NSDictionary(dictionary: ["1":"apple"])
    userDefault.setObject(dictionary, forKey: "NSDictionary")
    dictionary = userDefault.objectForKey("NSDictionary") as! NSDictionary
    
  • 2.系統(tǒng)對象的存儲與讀取
    系統(tǒng)對象實現(xiàn)存儲壳鹤,需要通過 archivedDataWithRootObject 方法轉(zhuǎn)換成 NSData 為載體,才可以存儲饰迹。下面以 UIImage 對象為例:
    let userDefault = NSUserDefaults.standardUserDefaults()
    //UIImage對象存儲
    //將對象轉(zhuǎn)換成NSData流
    let image = UIImage(named: "apple.png")
    let imageData:NSData = NSKeyedArchiver.archivedDataWithRootObject(image!)
    //存儲NSData對象
    userDefault.setObject(imageData, forKey: "imageData")
    //設(shè)置同步
    userDefault.synchronize()

     //UIImage對象讀取
     //獲取NSData
     let objData:NSData = userDefault.objectForKey("imageData") as! NSData
     //還原對象
     let myImage = NSKeyedUnarchiver.unarchiveObjectWithData(objData) as! UIImage
     print(myImage)
    
  • 3.自定義對象的存儲和讀取
    如果想要存儲自己定義的類芳誓,首先需要對該類實現(xiàn) NSCoding 協(xié)議來進行歸檔和反歸檔(序列化和反序列化)。即該類內(nèi)添加 func encodeWithCoder(_encoder:NSCoder) 方法和 init(coder decoder:NSCoder) 方法啊鸭,將屬性進行轉(zhuǎn)換锹淌。
    首先,我們定義一個model來存儲用戶的信息:
    //----- 自定義對象類 -----
    class UserInfo: NSObject {
    var name:String
    var phone:String

       //構(gòu)造方法
        init(name:String="",phone:String=""){
           self.name = name
           self.phone = phone
           super.init()
        }
    
       //從nsobject解析回來
        init(coder aDecoder:NSCoder!){
           self.name=aDecoder.decodeObjectForKey("Name") as! String
           self.phone=aDecoder.decodeObjectForKey("Phone") as! String
        }
    
        //編碼成object
       func encodeWithCoder(aCoder:NSCoder!){
          aCoder.encodeObject(name,forKey:"Name")
          aCoder.encodeObject(phone,forKey:"Phone")
       }
    }
    

下面我們來存儲自定義對象
let userDefault = NSUserDefaults.standardUserDefaults()

    //自定義對象存儲
    let model = UserInfo(name: "貓咪", phone: "18800000000")
    //實例對象轉(zhuǎn)換成NSData
    let modelData:NSData = NSKeyedArchiver.archivedDataWithRootObject(model)
    //存儲NSData對象
    userDefault.setObject(modelData, forKey: "myModel")
    //設(shè)置同步
    userDefault.synchronize()
    
    //自定義對象讀取
    let myModelData = userDefault.objectForKey("myModel") as! NSData
    _ = NSKeyedUnarchiver.unarchiveObjectWithData(myModelData) as! UserInfo

當我們退出登錄的時候赠制,我們可能會想清除本地的數(shù)據(jù)赂摆,我們可以通過removeObjectForKey() 方法可以刪除已保存的數(shù)據(jù)。當然如果這個存儲對象不存在也不會報錯。
NSUserDefaults.standardUserDefaults().removeObjectForKey("myModel")

注意:UserDefaults設(shè)置數(shù)據(jù)時烟号,不是立即寫入绊谭,而是根據(jù)時間戳定時地把緩存中的數(shù)據(jù)寫入本地磁盤。所以調(diào)用了set方法之后數(shù)據(jù)有可能還沒有寫入磁盤應用程序就終止了汪拥。出現(xiàn)以上問題达传,可以通過調(diào)用synchornize方法強制寫入userDefault.synchronize()
缺點: 本質(zhì)還是plist文件存儲,相對于plist文件存儲來講存儲數(shù)據(jù)更快捷.

三、NSKeyedArchiver(NSCoding)

如果對象是NSString喷楣、NSDictionary趟大、NSArray鹤树、NSData铣焊、NSNumber等類型,可以直接用NSKeyedArchiver進行歸檔和恢復罕伯,不是所有的對象都可以直接用這種方法進行歸檔曲伊,只有遵守了NSCoding協(xié)議的對象才可以。
NSCoding協(xié)議有2個方法:

encodeWithCoder:每次歸檔對象時追他,都會調(diào)用這個方法坟募。一般在這個方法里面指定如何歸檔對象中的每個實例變量,可以使用encodeObject:forKey:方法歸檔實例變量邑狸;
initWithCoder:每次從文件中恢復(解碼)對象時懈糯,都會調(diào)用這個方法。一般在這個方法里面指定如何解碼文件中的數(shù)據(jù)為對象的實例變量单雾,可以使用decodeObject:forKey方法解碼實例變量赚哗。
示例:
// 解檔 歸檔
required init?(coder aDecoder: NSCoder) {
    access_token = aDecoder.decodeObjectForKey("access_token") as! String
    expires_in = aDecoder.decodeDoubleForKey("expires_in")
    expiresDate = aDecoder.decodeObjectForKey("expiresDate") as! NSDate
    uid = aDecoder.decodeObjectForKey("uid") as! String
    name = aDecoder.decodeObjectForKey("name") as? String
    avatar_large = aDecoder.decodeObjectForKey("avatar_large") as? String
}
func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(access_token, forKey: "access_token")
    aCoder.encodeDouble(expires_in, forKey: "expires_in")
    aCoder.encodeObject(expiresDate, forKey: "expires_in")
    aCoder.encodeObject(uid, forKey: "uid")
    aCoder.encodeObject(name, forKey: "name")
    aCoder.encodeObject(avatar_large, forKey: "avatar_large")
}
歸檔路徑,swift中的類常量都是使用 static 來定義的
 static let accountPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).last!.stringByAppendingPathComponent("account.plist")
在OC中硅堆,要注意:
如果父類也遵守了NSCoding協(xié)議屿储,請注意:
應該在encodeWithCoder:方法中加上一句[super encodeWithCode:encode];確保繼承的實例變量也能被編碼,也能被歸檔渐逃;
應該在initWithCoder:方法中加上一句self = [super initWithCoder:decoder];確保繼承的實例變量也能被解碼够掠,即也能被恢復; 
但有時候可能想將多個對象寫入到同一個文件中茄菊,那么就要使用NSData來進行歸檔對象疯潭,NSData可以為一些數(shù)據(jù)提供臨時存儲空間,以便隨后寫入文件面殖,或者存放從磁盤讀取的文件內(nèi)容袁勺。
缺點: 歸檔的形式來保存數(shù)據(jù),只能一次性歸檔保存以及一次性解壓。所以只能針對小量數(shù)據(jù),而且對數(shù)據(jù)操作比較笨拙,即如果想改動數(shù)據(jù)的某一小部分,還是需要解壓整個數(shù)據(jù)或者歸檔整個數(shù)據(jù)畜普。

這一部分先整理到這里期丰,歡迎大家來指出不足~~~~~( _ )/~~拜拜

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子钝荡,更是在濱河造成了極大的恐慌街立,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件埠通,死亡現(xiàn)場離奇詭異赎离,居然都是意外死亡,警方通過查閱死者的電腦和手機端辱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門梁剔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舞蔽,你說我怎么就攤上這事荣病。” “怎么了渗柿?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵个盆,是天一觀的道長。 經(jīng)常有香客問我朵栖,道長颊亮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任陨溅,我火速辦了婚禮终惑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘门扇。我一直安慰自己雹有,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布悯嗓。 她就那樣靜靜地躺著件舵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪脯厨。 梳的紋絲不亂的頭發(fā)上铅祸,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音合武,去河邊找鬼临梗。 笑死,一個胖子當著我的面吹牛稼跳,可吹牛的內(nèi)容都是我干的盟庞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼汤善,長吁一口氣:“原來是場噩夢啊……” “哼什猖!你這毒婦竟也來了票彪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤不狮,失蹤者是張志新(化名)和其女友劉穎降铸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摇零,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡推掸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了驻仅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谅畅。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖噪服,靈堂內(nèi)的尸體忽然破棺而出毡泻,到底是詐尸還是另有隱情,我是刑警寧澤芯咧,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布牙捉,位于F島的核電站竹揍,受9級特大地震影響敬飒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芬位,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一无拗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昧碉,春花似錦英染、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至狭握,卻和暖如春闪金,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背论颅。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工哎垦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人恃疯。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓漏设,卻偏偏與公主長得像,于是被迫代替她去往敵國和親今妄。 傳聞我的和親對象是個殘疾皇子郑口,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容