應(yīng)同學(xué)之約陨仅,抽個時間立轧,寫篇關(guān)于 NSUserDefaults 實現(xiàn)保存數(shù)據(jù)的文章既鞠。
確實在項目中稼跳,雖然我們數(shù)據(jù)都是通過 API ,從后臺獲取物臂,但是很多時候瘟滨,我們需要做數(shù)據(jù)的本地緩保锯蛀,以便實現(xiàn)我們在沒有網(wǎng)絡(luò)的時候可以讀取本地數(shù)據(jù),而不致于說獲取不到數(shù)據(jù)馅精,整個頁面都空白的尷尬严嗜。還有如果是已經(jīng)有本地數(shù)據(jù)緩保,那么就不用每次都從通過網(wǎng)絡(luò)從后臺獲取洲敢,可以提高用戶體驗漫玄,也不至于浪費太多用戶的流量。我們就先討論這兩種簡單的情況压彭,其他特殊情況先排除開來睦优。
那么一般我們接觸到的本地存儲數(shù)據(jù)就幾種:
1,歸檔寫入沙盒
[str writeToFile:strFilePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
2哮塞,數(shù)據(jù)庫
可以借助 FMDB 等第三方庫去實現(xiàn)
3刨秆,NSUserDefaults
當(dāng)然大神如果有不同見解凳谦,歡迎提出忆畅。
這三種方法也是各有利弊,至于有什么不同尸执,跟優(yōu)缺點家凯,大家就自行百度、google吧如失,這里就不講太多绊诲。
今天我們就主要講一下第3種,最簡單粗暴的方法褪贵。
NSUserDefaults是一個單例掂之,在整個程序中只有一個實例對象,而且不同于我們自己定義的單例脆丁,因為他不會隨著這個程序的關(guān)閉而清除掉保存的數(shù)據(jù)世舰,下次你開啟程序,你之前保存的數(shù)據(jù)還是存在的槽卫,所以他可以用于本地數(shù)據(jù)的持久化跟压,而且簡單實用,我們也可以用來做為頁面間的傳值歼培。
NSUserDefaults支持的數(shù)據(jù)類型有:NSNumber(NSInteger震蒋、float、double)躲庄,NSString查剖,NSDate,NSArray噪窘,NSDictionary笋庄,BOOL.
其實他的用法非常簡單,跟我們平時使用字典基本一樣。一個key无切,一個Value荡短。
就比如說,我們在做登陸功能的時候哆键,基本上都要記住用戶的用戶名掘托,下次用戶再打開APP的時候,登陸的時候就不用再輸入用戶名了籍嘹。保存代碼如下:
//將NSString 對象存儲到 NSUserDefaults 中
NSString *userName = @"Jimmy.Lin";
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:userName forKey:@"saveUserName"];
那么下次打開登陸頁面的時候闪盔,就可以直接從NSUserDefaults中取出我們所保存的用戶名,直接使用了辱士。取出保存的數(shù)據(jù)代碼如下:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// 這里用到的 key 就是上次我們保存的時候所使用的 key
NSString *userName = [ defaults objectForKey:@"saveUserName"];
這里需要注意一點泪掀,就是如果你不想數(shù)據(jù)被覆蓋掉,那就需要使用不同的key颂碘,換句話講异赫,就是你現(xiàn)在里面已經(jīng)用"saveUserName"這個 key 保存了這一次的用戶名,如果你再使用同樣的這一個 key 保存數(shù)據(jù)的話头岔,那第二次的 value 會覆蓋掉第一次的 value塔拳,就是你再次取值的時候,取出來的就是第二次所保存的 value 了峡竣。
恩靠抑,講到這里,也許你會在使用過程中适掰,出現(xiàn)你寫了保存的代碼颂碧,但是你再次取值的時候卻取不出你之前保存的值,心里估計在罵娘 类浪,尼瑪载城,蘋果這么垃圾,自己的方法都出錯戚宦,還讓我們用个曙。
不用慌,NSUserDefaults偶爾不工作受楼,無法保存改動的數(shù)據(jù)的原因是垦搬,NSUserDefaults的機(jī)制是,過一段時間艳汽,會自動調(diào)用自己的函數(shù)synchronize去同步數(shù)據(jù)的猴贰。
而如果你是遇到和我此處類似的,改動數(shù)據(jù)后河狐,就退出程序了米绕,即在NSUserDefaults還沒來得及synchronize之前就退出程序瑟捣,就需要手動調(diào)用synchronize去保存數(shù)據(jù)了。
也就是說我們可以手動調(diào)用NSUserDefaults去執(zhí)行同步synchronize的動作栅干,以及時保存(修改了的)數(shù)據(jù)迈套。
那么為了確保萬無一失,我們把上面的保存代碼完善一下:
//將NSString 對象存儲到 NSUserDefaults 中
NSString *userName = @"Jimmy.Lin";
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:userName forKey:@"saveUserName"];
// 手動調(diào)用NSUserDefaults去執(zhí)行同步synchronize的動作碱鳞,以及時保存(修改了的)數(shù)據(jù)
[defaults synchronize];
OK桑李,再也不會出現(xiàn)坑爹的保存了數(shù)據(jù)取不出來值的情況了。
上面講的是簡單的保存簡單的字符串窿给,但是一般我們用后臺獲取到的數(shù)據(jù)都是 json 贵白,那么我們轉(zhuǎn)化完之后,就是字典或者數(shù)組了崩泡。那同樣的禁荒,這些數(shù)據(jù)也是一樣的保存。
// 保存數(shù)組
NSArray *arr = @[@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9"];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:arr forKey:@"saveArray"];
[defaults synchronize];
// 讀取我們保存的數(shù)組
NSArray *getArr = [defaults objectForKey:@"saveArray"];
// 打印出來看看是否正確
for (NSString *number in getArr) {
NSLog(@"number = %@",number);
}
打印結(jié)果如下角撞,正確呛伴,perfect
保存字典的用法一樣,這里就不重復(fù)了靴寂。
這里同樣有一點是需要特別注意的磷蜀,就是NSUserDefaults保存的數(shù)據(jù)全是不可變的!0倬妗!這一點是非常重要的污它,弄錯的話會有 bug 剖踊。到時候別說我是照著這方法保存的,拿出來用衫贬,卻崩了德澈。真的不是這個方法有問題!
當(dāng)然如果你只是保存的話是沒什么問題的固惯,也不會出現(xiàn)程序崩的情況:
按上面的代碼來的話梆造,運行完發(fā)現(xiàn)也能拿到我們要的結(jié)果,一切正常葬毫,那是不是覺得我們拿到的就是我們保存的可變數(shù)組镇辉?
那我們來驗證一下,如果真的是可變數(shù)據(jù)贴捡,那就可以添加刪除里面的元素:
再次運行忽肛,發(fā)現(xiàn)崩了±谜看一下程序崩潰原因:
看到這屹逛,很好理解吧础废。我們拿出來的是一個不可變數(shù)組!所以你添加元素的話肯定就崩啦罕模!
那如果我就是非要往拿出來的數(shù)組里面添加元素怎么辦呢评腺?很簡單,先轉(zhuǎn)成可變的淑掌,再添加歇僧,然后再用同樣的 key 保存進(jìn)去覆蓋掉之前的數(shù)據(jù)就搞定了:
OK,運行一下锋拖,打印出來的結(jié)果诈悍,已經(jīng)添加進(jìn)去了:
恩。好像用法就是這么簡單兽埃!看到這里是不是覺得這貨不就是一個字典嗎侥钳?所以你會用字典,這貨你也肯定會用 柄错,so easy 舷夺!
但是問題又來了,數(shù)據(jù)拿到的時候是 json 售貌,但是我們基本上都是轉(zhuǎn)換成 model 给猾,然后放進(jìn)數(shù)組里面,這樣方便后期處理跟使用颂跨「疑欤看一下上面講的 NSUserDefaults 能保存的數(shù)據(jù)類型,model 明顯是不行的恒削。但是別忘了 iOS 強(qiáng)大的數(shù)據(jù)轉(zhuǎn)化池颈,看一下,NSUserDefaults可以保存 NSData 的數(shù)據(jù)類型钓丰,那么就好辦了躯砰,只不過是需要多繞一個圈而已。那么怎么來實現(xiàn)這些自定義 model 的數(shù)據(jù)保存呢携丁?
還是讓我們一步一步來吧琢歇,首先,那先定義一個 model 類梦鉴,里面就簡單的加三個屬性吧李茫,為了方便,我們再添加一個初始化方法:
那我們要實現(xiàn)將 model 類轉(zhuǎn)換成 NSData 類型的尚揣,就必須歸檔涌矢,這里要實現(xiàn) 在.h 文件中遵循 NSCoding 協(xié)議,再 在 .m 中實現(xiàn) encodeWithCoder 方法 和
initWithCoder 方法就可以了 :
在 .m 文件里實現(xiàn)我們上面自定義的初始化方法快骗,跟 NSCoding 協(xié)議方法:
這樣做就可以將我們自定義的 model 類型轉(zhuǎn)變?yōu)?NSData 類型了娜庇。
直接上代碼吧:
看一下打印結(jié)果:
好了塔次,到這基本上也沒什么好講的了,都是很簡單的用法名秀,至少比第1種励负,第2種方法使用起來簡單太多了!
不過用 NSUserDefaults 保存數(shù)據(jù)的話匕得,要考慮的問題就是安全性继榆!至于你要用加密還是什么別的方法的話,我在這就不多加討論了汁掠,因為每個公司每個人處理的方法都不一樣略吨。
希望大家以后不會再為這簡單的數(shù)據(jù)保存問題頭疼了。