iOS數據存儲之對象歸檔
對象歸檔
對象歸檔是iOS中數據持久化的一種方式。
歸檔是指另一種形式的序列化,但它是任何對象都可以實現的更常規的類型。使用對模型對象進行歸檔的技術可以輕松將復雜的對象寫入文件,然后再從中讀取它們。對象歸檔后將得到一個后綴為.archive的文件
要使用對象歸檔,則歸檔的對象所屬類中實現的每個屬性都是標量,或者都是遵循NSCoding協議和NSCopying協議的某個類的實例,也就是說,在類的頭文件中需要添加如下語句
可以編解碼的條件是:對象要實現<>中的兩個協議,也就是說 要定義自己的編解碼規則,在頭文件中聲明遵循下列兩個協議
@interface BIDFourLines : NSObject <NSCoding, NSCopying>
遵循NSCoding協議
NSCoding中聲明了兩個方法,其中一個用於將對象編碼到歸檔中,另一個方法對歸檔解碼來創建一個新對象。
歸檔時要實現的方法為:
-(void)encodeWithCoder:(NSCoder *)aCoder;
可以使用KVC(Key-Value Coding,鍵值編碼)對對象和原生數據類型進行編碼和解碼。
若要對對象進行歸檔,必須使用正確的編碼方法將所有實例變量編碼成encoder。
在解碼時,實現一個通過NSCoder解碼的對象初始化方法,就可以恢復之前歸檔的對象。解碼時要實現的方法為:
-(id)initWithCoder:(NSCoder *)aDecode;
總的來說,只要對象實現了上面提到的兩個方法,就可以對所有對象的屬性進行編碼和解碼,然后就可以對對象進行歸檔,並且可以將其寫入歸檔或者從歸檔中讀取它們。
實現NSCopying協議
除了要遵循NSCoding協議外,還要求要使用歸檔的類實現NSCoping協議。這個協議中有一個copywithZone方法,
- (id)copyWithZone:(NSZone *)zone;
用來復制對象。其實現與inetWitheCoder非常相似,只需創建一個同一類的新實例,然后將新實例的所有屬性都設置為與該對象屬性相同的值。
案例
還是用之前那個案例來熟悉整個對象歸檔機制
首先仍然是獲取應用的Documents文件夾,然后在該文件夾下創建一個歸檔文件,代碼如下:
- (NSString *)dataFilePath { NSArray *paths = NSSearchPathForDirectoriesInDomain ( NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; return [documentsDirectory stringByAppendingPathComponent:@"data.archive"]; }
實現編碼:
定義了一個用於編碼的鍵,並且是常量
static NSString * const kRootKey = @"kRootKey";
//實現編碼,定義自己的翻譯規則,用kLinesKey來對lines進行編碼 - (void)encodeWithCoder:(NSCoder *)aCoder; { [aCoder encodeObject:self.lines forKey:kLinesKey]; }
實現了編碼后,就可以對對象進行歸檔了,對象歸檔發生在程序從前台退出之后。
具體的歸檔代碼如下:
- (void)applicationWillResignActive:(NSNotification *)notification { //獲取歸檔文件路徑 NSString *filePath = [self dataFilePath]; //把文本框組中的數據讀取出來,放進lines中 BIDFourLines *fourLines = [[BIDFourLines alloc] init]; fourLines.lines = [self.lineFields valueForKey:@"text"]; //聲明一個二進制流 data,開辟了一個空間 NSMutableData *data = [[NSMutableData alloc] init]; //聲明一個歸檔類,把歸檔類的內容放入data中 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; //用kRootKey進行編碼 [archiver encodeObject:fourLines forKey:kRootKey]; //結束編碼 [archiver finishEncoding]; //編碼結束后,歸檔類的內容已經放入data中了,此時data仍然駐留在內存中,需要寫入文件中 [data writeToFile:filePath atomically:YES]; }
實現解碼:
//實現解碼 - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; if (self) { self.lines = [aDecoder decodeObjectForKey:kLinesKey]; } return self; }
實現解碼后,就能夠對對象取消歸檔,這個過程發生在程序重新加載的時候,具體代碼如下:
- (void)viewDidLoad { [super viewDidLoad]; //查找歸檔文件路徑 NSString *filePath = [self dataFilePath]; if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { //用歸檔文件中的數據初始化data NSData *data = [[NSMutableData alloc] initWithContentsOfFile:filePath]; //聲明一個解歸檔對象 NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] //把data中的數據復制給解歸檔對象 initForReadingWithData:data]; //用kRootKey進行解碼 BIDFourLines *fourLines = [unarchiver decodeObjectForKey:kRootKey]; //結束解碼 [unarchiver finishDecoding]; //把lines中的數據填入到顯示的文本框中 for (int i = 0; i < 4; i++) { UITextField *theField = self.lineFields[i]; theField.text = fourLines.lines[i]; } } //訂閱消息,觀察自身是否退到后台,若從前台退出,則運行applicationWillResigneActive方法,進行歸檔 UIApplication *app = [UIApplication sharedApplication]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:app]; }
結果
歸檔后,將在應用的Documents文件夾下生成data.archive文件,如下圖
用Notepad++打開該文件會看到一堆亂碼,如下圖:
總結
我感覺對象歸檔方法有點類似於加密,要對某個對象歸檔,首先要定義一套對象的編碼機制,而這個編碼機制就可理解為“加密”,而對對象取消歸檔的時候,我們用對應的解碼機制來操作,也就是說,解碼機制就是與“加密”對應的“解密”。也可以把編碼和解碼理解為一套“翻譯規則”,在對象歸檔階段,編譯器按照用戶定義的“翻譯規則”將對象的各種屬性翻譯成底層的數據形式,在對象取消歸檔階段,編譯器又按照用戶定義的“翻譯規則”將之前保存的數據“翻譯”回有意義的數據。對象歸檔可以層層嵌套,也就是說,對象可以包含一個自定義的對象,只要被包含的這個對象也遵循NSCoding和NSCopying協議,有了這個嵌套,我們就可以把相對復雜的對象保存起來。
從本質上看,對象歸檔是以文件的形式保存數據的,使用的時候到相應路徑下去讀取文件內容就可以了。歸檔后的數據文件是保密的,無法直接查看,而屬性列表是明文,是可以直接查看的。