???? 最近我們可愛的測試工程師向我提出了一個關(guān)于用戶登錄狀態(tài)存儲的問題从隆,仔細(xì)看了下代碼古毛,正好整理了下NSKeyedArchiver的使用問題,所以形成了這些文字实牡,也是我在簡書的第一篇文章斋荞!
1.1關(guān)于數(shù)據(jù)的持久化存儲的幾種方式
??? 說到NSKeyedArchiver,也就先要了解下iOS開發(fā)中關(guān)于數(shù)據(jù)持久化存儲的幾種方式:1.屬性列表 2.對象歸檔 3.數(shù)據(jù)庫存儲(SQLite) 4.Apple提供的CoreData存儲工具荞雏,關(guān)于以上存儲方式的使用場景和各自的優(yōu)缺點,我在此就不再贅述了,今天主要談一談關(guān)于第二類存儲方式-對象歸檔的使用方法和特點凤优。
1.2什么是對象歸檔
歸檔是一種很常用的文件儲存方法悦陋,幾乎任何類型的對象都能夠被歸檔儲存(實際上是一種文件保存的形式)。
蘋果提供了NSKeyedArchiver和NSKeyedUnarchiver兩個類以供我們把對象序列化和反序列化筑辨,在存儲之前使用NSKeyedArchiver進(jìn)行序列化操作俺驶,并且寫入本地文件,在使用之前使用NSKeyedUnarchiver進(jìn)行反序列化的操作棍辕,以供提取使用暮现!
1.3什么場景下會使用到對象歸檔
在實際的開發(fā)過程中,我們會使用各種數(shù)據(jù)存儲的方式楚昭。
如果是簡單的進(jìn)行一些系統(tǒng)提供的類型栖袋,例如NSArray,NSDictionary,NSString,BOOL,NSInteger,NSfloat等基本數(shù)據(jù)類型或者對象,我們可以選擇系統(tǒng)提供的NSUserDefault這個單例抚太,使用簡單方便塘幅,但是僅僅只能對以上這些特定的數(shù)據(jù)格式進(jìn)行存儲,是否有些局限性尿贫?而且屬性屬性列表這種方式又是否安全呢电媳?可能這些這些條件NSUserDefault都無法滿足!
對于一些規(guī)律性的庆亡,量級比較大的數(shù)據(jù)匾乓,又有規(guī)律可循的數(shù)據(jù),我們可以選擇建表或者使用Apple提供的CoreData進(jìn)行持久化的存儲身冀!
那么如果數(shù)據(jù)的量級不是很大钝尸,沒有必要動用數(shù)據(jù)庫或者是CoreData這種大規(guī)模的殺傷性武器的時候括享,而且又對數(shù)據(jù)的安全性和持久性有那么些要求的時候搂根,我們最好去選擇對象序列化這種中等殺傷性工具了!
1.4對象歸檔的使用方法
使用歸檔的方法對系統(tǒng)提供的基本類型和基本對象進(jìn)行歸檔的操作铃辖,在這里不再闡述了剩愧,如果明白了對自定義對象的歸檔和解檔,那么系統(tǒng)的基本數(shù)據(jù)類型和基本對象的歸檔和解檔也就相對很easy了娇斩!
具體的使用方法我就借用目前我正在開發(fā)維護(hù)的代碼進(jìn)行以下說明:
目前我們產(chǎn)品的需求是在用戶登錄之后仁卷,就持久化存儲用戶的登陸相關(guān)信息,在后續(xù)使用中不需要再次登陸犬第。當(dāng)用戶沒有退出登錄锦积,卸載程序后,重新從App Store中現(xiàn)在app后歉嗓,同樣保持登陸狀態(tài)丰介。
如果僅僅是登陸之后的登陸狀態(tài),使用NSUserDefault完全可以實現(xiàn),只是安全性不是太好而已哮幢,但是當(dāng)用戶在登陸狀態(tài)卸載并且重新安裝app后,仍然要保持登陸狀態(tài)的話,那這個問題就值得思考下了鬼佣!
基于以上需求迅腔,也就是說我們要把用戶的登陸信息存儲在一個地方,這個地方要滿足的條件是1.持久化存儲柜某,用戶登錄后嗽元,再次打開app不需要重新進(jìn)行登錄????? 2.用戶在登錄的狀態(tài)下卸載app,再次重新安裝后喂击,仍然保持卸載前的登陸狀態(tài)还棱,也就是要完美重現(xiàn)卸載前的狀態(tài)!
基于以上的條件惭等,我們自然而然地聯(lián)想到蘋果的sandBox機(jī)制珍手,關(guān)于蘋果的sandBox機(jī)制,我們不再詳述辞做,最關(guān)鍵的一點是:在sandBox中的Document目錄下存儲的文件琳要,會根據(jù)用戶的appleID同步到apple的服務(wù)端,也就是說如果再次安裝app的時候秤茅,此app中的沙盒(sandBox)的Document目錄下的文件會被再次還原(用戶的app購買信息是和用戶的appleID綁定的)稚补,那么需求就被完美的滿足了,具體的代碼實現(xiàn)以及注意事項請繼續(xù)向下閱讀:
@interface IHomeSession : NSObject<NSCoding>
@property (nonatomic, strong) NSString *sessionId;? ? ? ? ????? //會話Id
@property (nonatomic, strong) NSDate? *lastSessionDate;? ? //記錄上一次請求時間
@property (nonatomic, strong) NSString *token;? ? ? ? ? ? ?????? //ut值
@property (nonatomic, strong) NSString *alias;? ? ? ? ? ? ??????? //設(shè)備別名
@property (nonatomic, strong) NSString *username;? ? ? ? ?? //用戶名
//序列化對象的單例
+ (IHomeSession *)sharedMemory;
//保存
- (void)save;
//獲取ssesionId
- (NSString *)getSessionId;
//重置
- (void)reset;
@end
以上是.h文件
具體屬性可以根據(jù)自己的需求進(jìn)行添加
提供獲取序列化單例的方法框喳,方便在項目全局進(jìn)行獲取和使用
提供保存(save),重置(reset)课幕,獲取sessionId(getSessionId)的api接口以供使用,也可以根據(jù)自己的需求添加api
最重要的一點五垮,是當(dāng)前類需要遵循NSCoding協(xié)議乍惊,NSCoding協(xié)議中有兩個方法,都是requred方法放仗,遵循該協(xié)議后润绎,必須實現(xiàn)。
以下是.m文件中NSCoding協(xié)議的具體實現(xiàn)
#pragma mark - NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_sessionId forKey:@"_sessionId"];
[aCoder encodeObject:_lastSessionDate forKey:@"_lastSessionDate"];
[aCoder encodeObject:_token forKey:@"_token"];
[aCoder encodeObject:_username forKey:@"_username"];
[aCoder encodeObject:_alias forKey:@"_alias"];
}
通過以上編碼的方法诞挨,對當(dāng)前類中的屬性進(jìn)行逐一的鍵值編碼莉撇!
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init])
{
_sessionId = [aDecoder decodeObjectForKey:@"_sessionId"];
_alias = [aDecoder decodeObjectForKey:@"_alias"];
_lastSessionDate = [aDecoder decodeObjectForKey:@"_lastSessionDate"];
_token = [aDecoder decodeObjectForKey:@"_token"];
_username = [aDecoder decodeObjectForKey:@"_username"];
}
return self;
}
通過以上解碼的方法,對當(dāng)前類中的屬性惶傻,根據(jù)鍵進(jìn)行逐一的逆向編碼棍郎,并返回一個當(dāng)前類的實例!
實現(xiàn)了以上的協(xié)議方法后银室,我們就可對當(dāng)前的類對象進(jìn)行歸檔和解檔的操作了:
首先我們規(guī)定一個歸檔文件在沙盒中的存儲路徑涂佃,寫在一個類方法中静秆,方便取用,為了滿足app重新安裝后仍然可以獲取到最后一次登陸信息的需求巡李,我們把文件存儲在沙盒中的第一個文件夾(Document)中抚笔,這樣可以在程序重新安裝后自動回復(fù),原理我在需求分析上已經(jīng)做了闡述侨拦!
+ (NSString *)path
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDir = [paths objectAtIndex:0];
NSString *dstPath = [documentDir stringByAppendingPathComponent:@"user.data"];
return dstPath;
}
歸檔的方法殊橙,我們集成在save的接口中:
- (void)save
{
[NSKeyedArchiver archiveRootObject:self toFile:[IHomeSession path]];
}
解檔的方法我們集成在單例的獲取中,一定要先查找對應(yīng)路徑下的文件是否存在狱从,如果存在進(jìn)行解檔操作膨蛮,不存在的話重新生成一個單例,這樣會增強(qiáng)程序的健壯性季研,防止誤取單例敞葛,造成程序崩潰!
+ (IHomeSession *)sharedMemory
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if ([[NSFileManager defaultManager] fileExistsAtPath:[IHomeSession path]]) {
instance = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSData dataWithContentsOfFile:[IHomeSession path]]];
}
else
{
instance = [[IHomeSession alloc] init];
}
});
return instance;
}
重置的接口中与涡,我們需要刪除本地文件惹谐,同時將單例中的各種屬性恢復(fù)到初始狀態(tài),然后將初始狀態(tài)下的對象保存歸檔驼卖!
- (void)reset
{
[[NSFileManager defaultManager] removeItemAtPath:[IHomeSession path] error:nil];
instance = [[IHomeSession alloc] init];
instance.sessionId = nil;
instance.lastSessionDate = nil;
instance.token = @"default";
instance.username = nil;
instance.alias = nil;
[instance save];
}
OK了氨肌,以上的關(guān)鍵代碼實現(xiàn)之后,歸檔和解檔的操作就可以完美實現(xiàn)了酌畜,在程序的任何一個地方都可以依托IHomeSeesion這個單例完成操作了怎囚,至于后續(xù)需要存儲的字段,可以根據(jù)產(chǎn)品需求依次添加了桥胞!
以上代碼的形成如有不夠嚴(yán)謹(jǐn)?shù)牡胤娇沂兀瑲g迎指出!謝謝贩虾!