遼寧seo站內(nèi)優(yōu)化合肥seo排名扣費(fèi)
在之前介紹的數(shù)據(jù)存儲(chǔ)方法中,不管是NSUserDefaults還是plist文件都不能對(duì)自定義對(duì)象進(jìn)行存儲(chǔ),OC提供的解歸檔恰好解決了這個(gè)問題
本片文章對(duì) iOS13 以后的版本 歸檔和解檔 進(jìn)行介紹。老版本的解歸檔見這篇文章:【iOS】文件(對(duì)象數(shù)據(jù))的歸檔和解檔,參考這篇文章對(duì)比學(xué)習(xí)會(huì)對(duì)解歸檔有更好的理解
目錄
- 簡(jiǎn)介
- 自定義對(duì)象的單個(gè)對(duì)象歸檔、解檔
- 多個(gè)對(duì)象解檔歸檔
- 嵌套類(復(fù)合類)
- 解檔 Success!!
- 注意
- MJExtension庫(kù)(JSONModel、YYModel)
簡(jiǎn)介
在iOS中,對(duì)象的序列化和反序列化分別使用NSKeyedArchiver和NSKeyedUnarchiver兩個(gè)類,我們可以把一個(gè)類對(duì)象進(jìn)行序列化然后保存到文件中,使用時(shí)再讀取文件,把內(nèi)容反序列化出來(lái)。這個(gè)過(guò)程通常也被稱為 對(duì)象的編碼(歸檔)和解碼(解檔)
- 歸檔 — 將對(duì)象以文件(二進(jìn)制數(shù)據(jù))的形式保存到磁盤上中(也稱序列化,持久化)
- 解檔 — 使用時(shí)從磁盤上讀取該文件的保存路徑,從而讀取文件的內(nèi)容(也稱反序列化)
歸檔一般保存自定義對(duì)象、自定義對(duì)象數(shù)組,由于自定義對(duì)象不具有歸檔的性質(zhì),所以只有遵循了NSCoding協(xié)議的類才可以歸檔
由于決大多數(shù)支持存儲(chǔ)數(shù)據(jù)的Foundation和Cocoa Touch類都遵循了NSCoding協(xié)議,因此,對(duì)于大多數(shù)OC提供的類來(lái)說(shuō),歸檔相對(duì)而言還是比較容易實(shí)現(xiàn)的。
對(duì)象歸檔的文件是保密的,在磁盤上無(wú)法查看文件中的內(nèi)容,而屬性列表是明文的,可以查看。通過(guò)文件歸檔產(chǎn)生的文件是不可見的,如果打開歸檔文件的話,內(nèi)容是亂碼的;ta不同于屬性列表和plist文件是可見的,正因?yàn)椴豢梢姷木壒?#xff0c;使得這種持久性的數(shù)據(jù)保存更有可靠性
自定義對(duì)象的單個(gè)對(duì)象歸檔、解檔
iOS 13中需要支持NSSecureCoding 協(xié)議
(父協(xié)議為NSCoding
)才能支持歸檔
- 自定義一個(gè)Person類并實(shí)現(xiàn)
NSCoding 協(xié)議
的方法
@interface Person : NSObject <NSSecureCoding>@property (nonatomic, copy)NSString* name;
@property (nonatomic, assign)int age;
@property (nonatomic, assign)double weight;@end@implementation Person//NSCoder是一個(gè)抽象類
//歸檔的協(xié)議方法
//將歸檔對(duì)象序列化
- (void)encodeWithCoder:(NSCoder *)coder {[coder encodeObject: self.name forKey: @"name"];[coder encodeInt: self.age forKey: @"age"];[coder encodeDouble: self.weight forKey: @"weight"];
}//解檔的協(xié)議方法
//將解檔對(duì)象反序列化
- (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的協(xié)議方法
+ (BOOL)supportsSecureCoding {return YES;
}
- 初始化待歸檔對(duì)象并進(jìn)行歸檔
+ (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;//歸檔成二進(jìn)制數(shù)據(jù)流NSError* error;NSData* data1 = [NSKeyedArchiver archivedDataWithRootObject: person requiringSecureCoding: YES error: &error];if (error) {NSLog(@"歸檔錯(cuò)誤:%@", error);return 0;}//寫入指定路徑(一般寫入到沙盒,這里方便演示存到一個(gè)新的文件夾)[data1 writeToFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver" atomically: YES];
Person對(duì)象被序列化后就會(huì)被保存在下方的文件中,但無(wú)法直接打開
通過(guò)終端命令打開后,可以看到內(nèi)容是經(jīng)過(guò)加密的,保證了數(shù)據(jù)的安全性
- 開始解檔
+ (nullable id)unarchivedObjectOfClass:(Class)cls fromData:(NSData *)data error:(NSError **)error;
//解檔此二進(jìn)制數(shù)據(jù)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(@"解檔錯(cuò)誤:%@", error);}NSLog(@"unarchiverPerson:%@", unarchiverPerson);
多個(gè)對(duì)象解檔歸檔
將多個(gè)對(duì)象歸檔在同一個(gè)文件中:
- 初始化待歸檔對(duì)象并進(jìn)行歸檔
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;//創(chuàng)建歸檔對(duì)象
NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding: NO];//進(jìn)行歸檔(編碼)操作
[archiver encodeObject: person1 forKey: @"personOne"];
[archiver encodeObject: person2 forKey: @"personTwo"];//將歸檔(序列化)后的數(shù)據(jù)寫入指定文件中
[archiver.encodedData writeToFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver" atomically: YES];//結(jié)束歸檔
[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);
嵌套類(復(fù)合類)
現(xiàn)對(duì)于Person
類,設(shè)置一個(gè)自定義對(duì)象dog
屬性,那么這個(gè)內(nèi)層的Dog
類也需要實(shí)現(xiàn)NSSecureCoding 協(xié)議
,否則程序會(huì)崩潰:
上面也提到過(guò),OC提供的類(比如這里的name
)已經(jīng)遵循了此協(xié)議,因此無(wú)需手動(dòng)操作,但自定義的Dog
類要手動(dòng)添加協(xié)議函數(shù):
@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;
}
以下是復(fù)合類解歸檔完整代碼:
#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;//創(chuàng)建歸檔對(duì)象NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding: NO];//進(jìn)行歸檔操作[archiver encodeObject: person1 forKey: @"personOne"];[archiver encodeObject: person2 forKey: @"personTwo"];//將歸檔(序列化)后的數(shù)據(jù)寫入指定文件中[archiver.encodedData writeToFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver" atomically: YES];//結(jié)束歸檔[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;
}
運(yùn)行結(jié)果如下:
解檔 Success!!
注意
如果需要?dú)w檔的類是某個(gè)自定義類的子類時(shí),就需要在歸檔和解檔之前實(shí)現(xiàn)父類的解檔和歸檔方法:[super encodeWithCoder: coder];
和[super initWithCoder: coder];
MJExtension庫(kù)(JSONModel、YYModel)
其實(shí)還可以使用MJExtension第三方庫(kù)實(shí)現(xiàn)解歸檔,這樣就可以不用寫復(fù)雜的NSCoding協(xié)議
,只需要一行代碼調(diào)用寫好的宏MJExtensionCodingImplementation就可以實(shí)現(xiàn)
MJExtension也和JSONModel、YYModel一樣,支持 JSON數(shù)據(jù)<->Model
的轉(zhuǎn)換同時(shí)也支持解歸檔,它們?cè)诖a量級(jí)上、性能優(yōu)化上各有優(yōu)缺點(diǎn),詳見這篇文章:
【YYModel,MJExtension,JSONModel對(duì)比】
具體的學(xué)習(xí),小編日后了解!