在之前介紹的數據存儲方法中,不管是NSUserDefaults還是plist文件都不能對自定義對象進行存儲,OC提供的解歸檔恰好解決了這個問題
本片文章對 iOS13 以后的版本 歸檔和解檔 進行介紹。老版本的解歸檔見這篇文章:【iOS】文件(對象數據)的歸檔和解檔,參考這篇文章對比學習會對解歸檔有更好的理解
目錄
- 簡介
- 自定義對象的單個對象歸檔、解檔
- 多個對象解檔歸檔
- 嵌套類(復合類)
- 解檔 Success!!
- 注意
- MJExtension庫(JSONModel、YYModel)
簡介
在iOS中,對象的序列化和反序列化分別使用NSKeyedArchiver和NSKeyedUnarchiver兩個類,我們可以把一個類對象進行序列化然后保存到文件中,使用時再讀取文件,把內容反序列化出來。這個過程通常也被稱為 對象的編碼(歸檔)和解碼(解檔)
- 歸檔 — 將對象以文件(二進制數據)的形式保存到磁盤上中(也稱序列化,持久化)
- 解檔 — 使用時從磁盤上讀取該文件的保存路徑,從而讀取文件的內容(也稱反序列化)
歸檔一般保存自定義對象、自定義對象數組,由于自定義對象不具有歸檔的性質,所以只有遵循了NSCoding協議的類才可以歸檔
由于決大多數支持存儲數據的Foundation和Cocoa Touch類都遵循了NSCoding協議,因此,對于大多數OC提供的類來說,歸檔相對而言還是比較容易實現的。
對象歸檔的文件是保密的,在磁盤上無法查看文件中的內容,而屬性列表是明文的,可以查看。通過文件歸檔產生的文件是不可見的,如果打開歸檔文件的話,內容是亂碼的;ta不同于屬性列表和plist文件是可見的,正因為不可見的緣故,使得這種持久性的數據保存更有可靠性
自定義對象的單個對象歸檔、解檔
iOS 13中需要支持NSSecureCoding 協議
(父協議為NSCoding
)才能支持歸檔
- 自定義一個Person類并實現
NSCoding 協議
的方法
@interface Person : NSObject <NSSecureCoding>@property (nonatomic, copy)NSString* name;
@property (nonatomic, assign)int age;
@property (nonatomic, assign)double weight;@end@implementation Person//NSCoder是一個抽象類
//歸檔的協議方法
//將歸檔對象序列化
- (void)encodeWithCoder:(NSCoder *)coder {[coder encodeObject: self.name forKey: @"name"];[coder encodeInt: self.age forKey: @"age"];[coder encodeDouble: self.weight forKey: @"weight"];
}//解檔的協議方法
//將解檔對象反序列化
- (instancetype)initWithCoder:(NSCoder *)coder {self = [super init];if (self) {self.name = [coder decodeObjectForKey: @"name"];self.age = [coder decodeIntForKey: @"age"];self.weight = [coder decodeDoubleForKey: @"weight"];}return self;
}@end//NSSecureCoding的協議方法
+ (BOOL)supportsSecureCoding {return YES;
}
- 初始化待歸檔對象并進行歸檔
+ (nullable NSData *)archivedDataWithRootObject:(id)object requiringSecureCoding:(BOOL)requiresSecureCoding error:(NSError **)error;
Person* person = [[Person alloc] init];person.name = @"XY";person.age = 20;person.weight = 125.0;//歸檔成二進制數據流NSError* error;NSData* data1 = [NSKeyedArchiver archivedDataWithRootObject: person requiringSecureCoding: YES error: &error];if (error) {NSLog(@"歸檔錯誤:%@", error);return 0;}//寫入指定路徑(一般寫入到沙盒,這里方便演示存到一個新的文件夾)[data1 writeToFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver" atomically: YES];
Person對象被序列化后就會被保存在下方的文件中,但無法直接打開
通過終端命令打開后,可以看到內容是經過加密的,保證了數據的安全性
- 開始解檔
+ (nullable id)unarchivedObjectOfClass:(Class)cls fromData:(NSData *)data error:(NSError **)error;
//解檔此二進制數據error = nil;NSData* data2 = [NSData dataWithContentsOfFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver"];Person* unarchiverPerson = (Person *)[NSKeyedUnarchiver unarchivedObjectOfClass: [Person class] fromData: data2 error: &error];if (error) {NSLog(@"解檔錯誤:%@", error);}NSLog(@"unarchiverPerson:%@", unarchiverPerson);
多個對象解檔歸檔
將多個對象歸檔在同一個文件中:
- 初始化待歸檔對象并進行歸檔
Person* person1 = [[Person alloc] init];
person1.name = @"XY";
person1.age = 20;
person1.weight = 125.0;
Dog* dog1 = [[Dog alloc] init];
dog1.name = @"Bruce";
person1.dog = dog1; Person* person2 = [[Person alloc] init];
person2.name = @"Jacky";
person2.age = 21;
person2.weight = 130.0;
Dog* dog2 = [[Dog alloc] init];
dog2.name = @"Oudy";
person2.dog = dog2;//創建歸檔對象
NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding: NO];//進行歸檔(編碼)操作
[archiver encodeObject: person1 forKey: @"personOne"];
[archiver encodeObject: person2 forKey: @"personTwo"];//將歸檔(序列化)后的數據寫入指定文件中
[archiver.encodedData writeToFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver" atomically: YES];//結束歸檔
[archiver finishEncoding];
- 依次解檔
//解檔
NSData* data = [NSData dataWithContentsOfFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver"];
NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData: data error: nil];
unarchiver.requiresSecureCoding = NO;Person* unchiverPerson1 = [unarchiver decodeObjectForKey: @"personOne"];
NSLog(@"%@ %d %lf %@", unchiverPerson1.name, unchiverPerson1.age, unchiverPerson1.weight, unchiverPerson1.dog.name);
Person* unchiverPerson2 = [unarchiver decodeObjectForKey: @"personTwo"];
NSLog(@"%@ %d %lf %@", unchiverPerson2.name, unchiverPerson2.age, unchiverPerson2.weight, unchiverPerson2.dog.name);
嵌套類(復合類)
現對于Person
類,設置一個自定義對象dog
屬性,那么這個內層的Dog
類也需要實現NSSecureCoding 協議
,否則程序會崩潰:
上面也提到過,OC提供的類(比如這里的name
)已經遵循了此協議,因此無需手動操作,但自定義的Dog
類要手動添加協議函數:
@interface Dog : NSObject <NSSecureCoding>@property (nonatomic, strong)NSString* name;@end- (void)encodeWithCoder:(NSCoder *)coder {[coder encodeObject: self.name forKey: @"dogName"];
}- (instancetype)initWithCoder:(NSCoder *)coder {self = [super init];if (self) {self.name = [coder decodeObjectForKey: @"dogName"];}return self;
}+ (BOOL)supportsSecureCoding {return YES;
}
以下是復合類解歸檔完整代碼:
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Dog.h"int main(int argc, const char * argv[]) {@autoreleasepool {Person* person1 = [[Person alloc] init];person1.name = @"XY";person1.age = 20;person1.weight = 125.0;Dog* dog1 = [[Dog alloc] init];dog1.name = @"Bruce";person1.dog = dog1;Person* person2 = [[Person alloc] init];person2.name = @"Jacky";person2.age = 21;person2.weight = 130.0;Dog* dog2 = [[Dog alloc] init];dog2.name = @"Oudy";person2.dog = dog2;//創建歸檔對象NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding: NO];//進行歸檔操作[archiver encodeObject: person1 forKey: @"personOne"];[archiver encodeObject: person2 forKey: @"personTwo"];//將歸檔(序列化)后的數據寫入指定文件中[archiver.encodedData writeToFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver" atomically: YES];//結束歸檔[archiver finishEncoding];//解檔NSData* data = [NSData dataWithContentsOfFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver"];NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData: data error: nil];unarchiver.requiresSecureCoding = NO;Person* unchiverPerson1 = [unarchiver decodeObjectForKey: @"personOne"];NSLog(@"%@ %d %lf %@", unchiverPerson1.name, unchiverPerson1.age, unchiverPerson1.weight, unchiverPerson1.dog.name);Person* unchiverPerson2 = [unarchiver decodeObjectForKey: @"personTwo"];NSLog(@"%@ %d %lf %@", unchiverPerson2.name, unchiverPerson2.age, unchiverPerson2.weight, unchiverPerson2.dog.name);return 0;
}
運行結果如下:
解檔 Success!!
注意
如果需要歸檔的類是某個自定義類的子類時,就需要在歸檔和解檔之前實現父類的解檔和歸檔方法:[super encodeWithCoder: coder];
和[super initWithCoder: coder];
MJExtension庫(JSONModel、YYModel)
其實還可以使用MJExtension第三方庫實現解歸檔,這樣就可以不用寫復雜的NSCoding協議
,只需要一行代碼調用寫好的宏MJExtensionCodingImplementation就可以實現
MJExtension也和JSONModel、YYModel一樣,支持 JSON數據<->Model
的轉換同時也支持解歸檔,它們在代碼量級上、性能優化上各有優缺點,詳見這篇文章:
【YYModel,MJExtension,JSONModel對比】
具體的學習,小編日后了解!