iOS 常用數(shù)據(jù)存儲(chǔ)之NSKeyedArchiver(歸檔)

在實(shí)際的項(xiàng)目中,數(shù)據(jù)存儲(chǔ)是每個(gè)軟件開發(fā)者不可避免會(huì)涉及到的話題。對(duì)于iOS開發(fā)者而言,我們首先關(guān)心的就是app是把數(shù)據(jù)存儲(chǔ)在哪里的。

一.關(guān)于沙盒機(jī)制。

和Android系統(tǒng)不同的是,iOS系統(tǒng)使用的是特有的數(shù)據(jù)安全策略:沙盒機(jī)制。所謂沙盒機(jī)制是指:系統(tǒng)會(huì)為每個(gè)APP分配一塊獨(dú)立的存儲(chǔ)空間,用于存儲(chǔ)圖像,圖標(biāo),聲音,映像,屬性列表,文本等文件,并且在默認(rèn)情況下每個(gè)APP只能訪問自己的空間。

怎么查看模擬器中APP的沙盒的目錄結(jié)構(gòu)?
在程序中執(zhí)行如下語句,得到沙盒的路徑:

NSLog(@"%@",NSHomeDirectory());

打印的結(jié)果是:

 /Users/XXX/Library/Developer/CoreSimulator/Devices/41D208F9-4E93-48EC-A254-10EA246048DF/data/Containers/Data/Application/8AD61414-4125-408A-B0FF-DE451973E2C8

在Finder中,F(xiàn)inder>前往>前往文件夾。將上面打印出的路徑張貼到輸入框內(nèi),然后點(diǎn)擊前往。就會(huì)出現(xiàn)如下內(nèi)容:

沙盒目錄結(jié)構(gòu).png

我們可以看到三個(gè)文件夾:Documents, Library, tmp。

  1. Documents: 用于保存應(yīng)用運(yùn)行時(shí)生成的需要持久化、非常大的或者需要頻繁更新的數(shù)據(jù),iTunes會(huì)自動(dòng)備份該目錄 .
    //獲取目錄位置的代碼如下
    NSArray *documentDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    //上面的documentDirectory是只有一個(gè)元素的數(shù)組,還需要以下代碼取出路徑
    NSString *myDocPath = [documentDirectory objectAtIndex:0];
    //或者 NSString *myDocPath = [documentDirectory lastObject];
  2. Libaray: 用于存儲(chǔ)程序的默認(rèn)設(shè)置和其他狀態(tài)信息,iTunes會(huì)自動(dòng)備份該目錄。Libaray/下主要有兩個(gè)文件夾:Libaray/Caches和Libaray/Preferences。
    //獲取目錄位置的代碼如下:
    NSArray *libraryDirectory = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask,YES);
    NSString * libraryDirectory = [libraryDirectory lastObject];
  1. Libaray/Caches:存放緩存文件,iTunes不會(huì)備份此目錄,此目錄下文件不會(huì)在應(yīng)用退出刪除,一般存放體積比較大,不是很重要的資源。
    //獲取目錄位置的代碼如下:
    NSArray *cachesDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
    NSString *cachesDirectory = [cachesDirectory lastObject];
  1. Libaray/Preferences:保存應(yīng)用的所有偏好設(shè)置,ios的Settings(設(shè)置)應(yīng)用會(huì)在該目錄中查找應(yīng)用的設(shè)置信息,iTunes會(huì)自動(dòng)備份該目錄。
    //獲取目錄位置的代碼如下:
    NSArray * preferencesDirectory = NSSearchPathForDirectoriesInDomains(NSPreferencesDirectory,NSUserDomainMask,YES);
    NSString * preferencesDirectory = [preferencesDirectory lastObject];
  2. tmp: 保存應(yīng)用運(yùn)行時(shí)所需的臨時(shí)數(shù)據(jù),使用完畢后再將相應(yīng)的文件從該目錄刪除,應(yīng)用沒有運(yùn)行時(shí),系統(tǒng)也可能會(huì)自動(dòng)清理該目錄下的文件,iTunes不會(huì)同步該目錄,iPhone重啟時(shí)該目錄下的文件會(huì)丟失。
    //獲取目錄位置的代碼如下:
    NSString *tmp = NSTemporaryDirectory();

既然存的地方清楚了,我們接下來聊聊iOS常見的存儲(chǔ)方式:

二.常見的存儲(chǔ)方式。

我們常見的有四種存儲(chǔ)方式:用NSUserDefaults存儲(chǔ)配置信息 ,用NSKeyedArchive歸檔的形式來保存對(duì)象數(shù)據(jù), 文件沙盒存儲(chǔ) , 數(shù)據(jù)庫(kù)存儲(chǔ) 。

1. 用NSUserDefaults存儲(chǔ)配置信息

該方法被設(shè)計(jì)用來存儲(chǔ)設(shè)備和應(yīng)用的配置、屬性、用戶的信息,它通過一個(gè)工廠方法返回默認(rèn)的實(shí)例對(duì)象。它實(shí)際上是存儲(chǔ)于文件沙盒中的一個(gè).plist文件,并且沒有被系統(tǒng)加密,只是ios6以后不是存于常用的文檔目錄下,所以不破解系統(tǒng)是看不到。
該文件的可以存儲(chǔ)的數(shù)據(jù)類型包括:NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary。如果要存儲(chǔ)其他類型,則需要轉(zhuǎn)換為前面的類型,才能用NSUserDefaults存儲(chǔ)。

用該方式存數(shù)據(jù)的代碼如下 :

 NSString *teleNum =  @"1868XX23763"; 
 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
 [userDefaults setObject: teleNum forKey:@"teleNum"];

用該方式取數(shù)據(jù)的代碼如下 :

 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
 NSString *teleNum = [userDefaults objectForKey:@"teleNum"]];
2. 文件沙盒存儲(chǔ)

主要存儲(chǔ)非機(jī)密數(shù)據(jù),大的數(shù)據(jù),如圖片。

存文件的操作步驟如下:

(1). 獲得文件即將保存的路徑
方法一:

NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,  NSUserDomainMask,YES);
NSString *ourDocumentPath =[documentPaths objectAtIndex:0];

方法二:

NSString *sandboxPath = NSHomeDirectory();
NSString *documentPath = [sandboxPath
        stringByAppendingPathComponent:@"Documents"];

(2). 生成在該路徑下的文件

NSString *FileName=[documentDirectory stringByAppendingPathComponent:fileName];//fileName就是保存文件的文件名 

(3). 往文件中寫入數(shù)據(jù)

[data writeToFile:FileName atomically:YES];//將NSData類型對(duì)象data寫入文件,文件名為FileName
從沙盒中取出文件:

取就比較簡(jiǎn)單,只需下面一行代碼!

NSData data=[NSData dataWithContentsOfFile:FileName options:0 error:NULL];//從FileName中讀取出數(shù)據(jù)
3. 用NSKeyedArchive歸檔的形式來保存對(duì)象數(shù)據(jù)

由于筆者暫時(shí)沒有用過數(shù)據(jù)庫(kù)存儲(chǔ),所以就不在這里幫門弄斧了。如文章題目,今天主要想談的是第三種方法,即用NSKeyedArchive歸檔。下面將用一個(gè)簡(jiǎn)單的項(xiàng)目來講解。

使用情景:在我們的實(shí)際開發(fā)過程中,為了更好的用戶體驗(yàn),可能需要在APP上存儲(chǔ)用戶的某些信息,比如淘寶APP會(huì)記錄用戶曾經(jīng)填寫過的收貨地址。這種數(shù)據(jù)不方便用前面提到的NSUserDefaults來存儲(chǔ):一是,因?yàn)榈刂窋?shù)據(jù)是一個(gè)模型,NSUserDefaults只能用于NSString、NSArray、NSDictionary等常用的數(shù)據(jù)類型;二是,用戶還會(huì)經(jīng)常對(duì)它進(jìn)行增刪改等操作,NSUserDefaults無法滿足。
此時(shí),用NSKeyedArchive歸檔的形式來存儲(chǔ)地址模型數(shù)據(jù)最符合要求。

效果圖.png
(1)創(chuàng)建地址模型CLVoiceApplyAddressModel

CLVoiceApplyAddressModel.h文件

#import <Foundation/Foundation.h>

@interface CLVoiceApplyAddressModel : NSObject<NSCoding>
//用于存儲(chǔ)多個(gè)地址時(shí),標(biāo)記用戶選中的狀態(tài)
@property (nonatomic, copy) NSString *state;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *phoneNum;
@property (nonatomic ,copy) NSString *mainAddress;
@property (nonatomic ,copy) NSString *detailAddress;

+(instancetype)AddressModelWithDict:(NSDictionary *)dict;
-(instancetype)initAddressModelWithDict:(NSDictionary *)dict;
@end

CLVoiceApplyAddressModel.m文件

  #import "CLVoiceApplyAddressModel.h"

  @implementation CLVoiceApplyAddressModel

-(void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:_state forKey:@"state"];
    [aCoder encodeObject:_name forKey:@"name"];
    [aCoder encodeObject:_phoneNum forKey:@"phoneNum"];
    [aCoder encodeObject:_mainAddress forKey:@"mainAddress"];
    [aCoder encodeObject:_detailAddress forKey:@"detailAddress"];
}
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self) {
    self.state= [aDecoder decodeObjectForKey:@"state"];
    self.name = [aDecoder decodeObjectForKey:@"name"];
    self.phoneNum = [aDecoder decodeObjectForKey:@"phoneNum"];
    self.mainAddress = [aDecoder decodeObjectForKey:@"mainAddress"];
    self.detailAddress = [aDecoder decodeObjectForKey:@"detailAddress"];
}else{
    return nil;
}
return self;
}
+(instancetype)AddressModelWithDict:(NSDictionary *)dict{
return [[self alloc] initAddressModelWithDict:dict];
}

-(instancetype)initAddressModelWithDict:(NSDictionary *)dict{
if (self = [super init]) {
    self.state =[dict objectForKey:@"state"];
    self.name =[dict objectForKey:@"name"];
    self.phoneNum =[dict objectForKey:@"phoneNum"];
    self.mainAddress =[dict objectForKey:@"mainAddress"];
    self.detailAddress =[dict objectForKey:@"detailAddress"];
}
return self;
}
@end

為了實(shí)現(xiàn)對(duì)歸檔地址的操作,我們專門創(chuàng)建了一個(gè)工具類:CLInvoiceApplyAddressModelTool。

#import <Foundation/Foundation.h>
@class CLVoiceApplyAddressModel;

@interface CLInvoiceApplyAddressModelTool : NSObject

+(NSArray *)allAddressInfo;
+(CLVoiceApplyAddressModel *)currentSelectedAddress;
+(void)update;
+(void)updateAddressInfoAfterDeleted;

+(void)setSelectedAddressByNewInfoArray:(NSArray *)infoArray;
+(void)addInfo:(CLVoiceApplyAddressModel *)info;
+(void)removeInfoAtIndex:(NSUInteger)index;
+(void)updateInfoAtIndex:(NSUInteger)index withInfo:(CLVoiceApplyAddressModel *)info;
+(void)removeAllInfo;
@end


#import "CLInvoiceApplyAddressModelTool.h"
#import "CLVoiceApplyAddressModel.h"
#define AddressInfosPath  [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"addressInfo1.data"]

@implementation CLInvoiceApplyAddressModelTool
static NSMutableArray *_addressInfos;
+(NSArray *)allAddressInfo{
    _addressInfos = [NSKeyedUnarchiver unarchiveObjectWithFile:AddressInfosPath];
    if (!_addressInfos) _addressInfos = [NSMutableArray array];
    return _addressInfos;
}
+(CLVoiceApplyAddressModel *)currentSelectedAddress{
    CLVoiceApplyAddressModel *currentAddress;
BOOL hasSelectedAddress = NO;
if ([self allAddressInfo].count) {
    for (CLVoiceApplyAddressModel *info in _addressInfos) {
        if ([info.state isEqualToString:@"1"]) {
            currentAddress = info;
            hasSelectedAddress = YES;
            break;
        };
    }
}else if([self allAddressInfo].count == 0 || hasSelectedAddress)
{
    currentAddress = nil;
}
return currentAddress;
}
+(void)update{
[NSKeyedArchiver archiveRootObject:_addressInfos toFile:AddressInfosPath];
}
+(void)updateAddressInfoAfterDeleted{
if (_addressInfos.count) {
    if (![self currentSelectedAddress]) {
        CLVoiceApplyAddressModel *info = [CLInvoiceApplyAddressModelTool allAddressInfo][0];
        info.state = @"1";
        [CLInvoiceApplyAddressModelTool updateInfoAtIndex:0 withInfo:info];
    }
}
  }
+(void)setSelectedAddressByNewInfoArray:(NSArray *)infoArray{
[NSKeyedArchiver archiveRootObject:infoArray toFile:AddressInfosPath];
}
+(void)addInfo:(CLVoiceApplyAddressModel *)info{
if (!_addressInfos.count) {
    _addressInfos = [NSMutableArray array];
}
for (CLVoiceApplyAddressModel *oldInfo in _addressInfos ) {
    oldInfo.state = @"0";
}
[_addressInfos insertObject:info atIndex:0];
[self update];
}
+ (void)removeInfoAtIndex:(NSUInteger)index {
[_addressInfos removeObjectAtIndex:index];
[self update];
}
+ (void)removeAllInfo{
[_addressInfos removeAllObjects];
[self update];
}
+ (void)updateInfoAtIndex:(NSUInteger)index withInfo:(CLVoiceApplyAddressModel *)info {
[_addressInfos replaceObjectAtIndex:index withObject:info];
[self update];
}
@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容