日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

iOS - 数据的归档和反归档

發(fā)布時(shí)間:2025/3/20 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS - 数据的归档和反归档 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

由于機(jī)緣巧合,有了對(duì) iOS 基礎(chǔ)知識(shí)查漏補(bǔ)缺的機(jī)會(huì),為了建立更加完善的 iOS 開發(fā)知識(shí)體系,這里將會(huì)更新一段時(shí)間的基礎(chǔ)知識(shí)相關(guān)的文章。本篇文章主要介紹利用 NSKeyedArchiver 和 NSKeyedUnarchiver 對(duì)數(shù)據(jù)進(jìn)行歸檔和反歸檔的操作。

文章目錄

  • 前言
  • 1. 基本認(rèn)知
    • 1.1 歸檔
    • 1.2 反歸檔
    • 1.3 歸檔的應(yīng)用場(chǎng)景
  • 2. 歸檔與反歸檔的具體操作
    • 2.1 對(duì) Foundation 對(duì)象進(jìn)行歸檔
      • 歸檔操作
      • 反歸檔操作
      • 應(yīng)用實(shí)例
    • 2.2 對(duì)自定類型對(duì)象進(jìn)行歸檔
      • 利用 Runtime 優(yōu)化
    • 2.3 對(duì)多種類型的對(duì)象進(jìn)行歸檔
  • 3. 解決方法已被棄用問(wèn)題
    • 3.1 舊方法被棄用的原因
    • 3.2 新方法的使用
  • 參考資料

1. 基本認(rèn)知

1.1 歸檔

歸檔是指對(duì)需要存儲(chǔ)的對(duì)象數(shù)據(jù)進(jìn)行編碼,然后以文件的形式存儲(chǔ)到沙盒的 Documents 目錄下,實(shí)現(xiàn)數(shù)據(jù)的持久化存儲(chǔ)。歸檔的數(shù)據(jù)是經(jīng)過(guò)加密的,打開沙盒中歸檔的文件,看到的內(nèi)容會(huì)是亂碼,因此具有一定的可靠性。

1.2 反歸檔

反歸檔是歸檔的逆過(guò)程,是指讀取沙盒中經(jīng)過(guò)歸檔的文件,經(jīng)過(guò)解碼后重新轉(zhuǎn)化為對(duì)象數(shù)據(jù),提供給程序使用。

1.3 歸檔的應(yīng)用場(chǎng)景

  • 用于數(shù)據(jù)的離線緩存,在手機(jī)網(wǎng)絡(luò)正常的時(shí)候,通過(guò)歸檔緩存一些數(shù)據(jù)。等到手機(jī)斷網(wǎng)的時(shí)候,用這些歸檔數(shù)據(jù)進(jìn)行展示。
  • 進(jìn)行持久化存儲(chǔ),可以存出用戶的一些搜索記錄之類的一些數(shù)據(jù)。

2. 歸檔與反歸檔的具體操作

2.1 對(duì) Foundation 對(duì)象進(jìn)行歸檔

歸檔操作

歸檔操作需要用上 NSKeyedArchiver 類提供的類方法:

/*** 對(duì)對(duì)象進(jìn)行歸檔操作* * @param rootObject 需要進(jìn)行歸檔的對(duì)象* @param path 在沙盒中的存儲(chǔ)路徑* @return 歸檔操作是否成功*/ + (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;

PS:被歸檔的對(duì)象需要遵循 NSCoding 協(xié)議,Foundation 框架中的常用類都滿足這個(gè)前提,如 NSString、NSArray、NSDictionary等。

反歸檔操作

反歸檔操作則需要用上 NSKeyedUnarchiver 類提供的類方法:

/*** 對(duì)對(duì)象進(jìn)行反歸檔操作* * @param path 在沙盒中的存儲(chǔ)路徑* @return 反歸檔獲取到的對(duì)象*/ + (nullable id)unarchiveObjectWithFile:(NSString *)path;

應(yīng)用實(shí)例

這里可以看一個(gè)簡(jiǎn)單的例子,我們創(chuàng)建一個(gè)數(shù)組對(duì)象,然后將該對(duì)象歸檔到沙盒的 Documents 目錄下,文件命名為 arr.archiver。

NSArray *arr = @[@"Kobe", @"James", @"Iverson", @"Curry"]; // 在沙盒中獲取 Documents 路徑 NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; // 歸檔對(duì)象的最終存儲(chǔ)位置 NSString *arrPath = [docPath stringByAppendingPathComponent:@"arr.archiver"]; // 后綴名可隨意填寫BOOL isSussess = [NSKeyedArchiver archiveRootObject:arr toFile:arrPath]; NSLog(@"%d", isSussess ? @"Sussess" : @"Fail");

在需要用到歸檔數(shù)據(jù)的時(shí)候,我們將文件 arr.archiver 進(jìn)行反歸檔,還原成數(shù)組對(duì)象,并將其輸出。

NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *arrPath = [docPath stringByAppendingPathComponent:@"arr.archiver"];// 通過(guò)反歸檔獲取到在沙盒中存儲(chǔ)的對(duì)象信息 NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithFile:arrPath]; // 遍歷獲取到的數(shù)組,驗(yàn)證結(jié)果 [arr enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {NSLog(@"%lu - %@", idx, obj); }];

2.2 對(duì)自定類型對(duì)象進(jìn)行歸檔

要想對(duì)自定義類型對(duì)象進(jìn)行歸檔,其實(shí)流程和上面一樣,但是需要讓自定義的類遵循 NSCoding 協(xié)議。

// 讓自定義的類遵循 NSCoding 協(xié)議 @interface VGPerson : NSObject <NSCoding>@property (nonatomic, assign) NSInteger age; @property (nonatomic, copy) NSString *name;@end

在 .m 文件中,實(shí)現(xiàn) NSCoding 協(xié)議中所要求的兩個(gè)方法。

@implementation VGPerson- (nullable instancetype)initWithCoder:(NSCoder *)coder {if (self = [super init]) {self.age = [coder decodeIntegerForKey:@"age"];self.name = [coder decodeObjectForKey:@"name"];}return self; }- (void)encodeWithCoder:(NSCoder *)coder {[coder encodeInteger:self.age forKey:@"age"];[coder encodeObject:self.name forKey:@"name"]; }@end

在完成些操作之后,我們就可以對(duì)自定義對(duì)象進(jìn)行歸檔了,操作和上面提到的 Foundation 框架對(duì)象的歸檔操作一樣

顯然,我們不難理解,Foundation 框架對(duì)象之所以可以直接進(jìn)行歸檔,是因?yàn)橄到y(tǒng)已經(jīng)幫我們實(shí)現(xiàn)了了 NSCoding 協(xié)議,可以看下下面 NSString 的類聲明。

// 系統(tǒng)實(shí)現(xiàn)了 NSSecureCoding 協(xié)議,它繼承自 NSCoding @interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>

利用 Runtime 優(yōu)化

上面的常規(guī)寫法并沒有什么問(wèn)題,但是當(dāng)這個(gè)類的屬性比較多,而且改動(dòng)頻繁的時(shí)候,我們編碼比較麻煩,需要頻繁改動(dòng)。這個(gè)時(shí)候,我們可以利用 Rumtime 對(duì)寫法進(jìn)行優(yōu)化。

首先,我們利用 runtime 獲取到該類的 objc_ivar_list(實(shí)例變量列表),然后遍歷該 objc_ivar_list,獲取每個(gè)實(shí)例變量的名稱,再結(jié)合 KVO 技術(shù)進(jìn)行存取值操作,就可以實(shí)現(xiàn)一次編碼,長(zhǎng)久使用的效果。

#import <objc/runtime.h>@implementation VGPerson- (nullable instancetype)initWithCoder:(NSCoder *)coder {if (self = [super init]) {unsigned int count = 0;// 獲取該類的 objc_ivar_listIvar *ivars = class_copyIvarList([VGPerson class], &count);for (int i = 0; i < count; i++) {Ivar ivar = ivars[i];const char *name = ivar_getName(ivar);NSString *key = [NSString stringWithUTF8String:name];// 根據(jù) key 解碼獲取屬性值id value = [coder decodeObjectForKey:key];// 利用 KVC 進(jìn)行賦值[self setValue:value forKey:key];}}return self; }- (void)encodeWithCoder:(NSCoder *)coder {unsigned int count = 0;Ivar *ivars = class_copyIvarList([VGPerson class], &count);for (int i = 0; i < count; i++) {Ivar ivar = ivars[i];const char *name = ivar_getName(ivar);NSString *key = [NSString stringWithUTF8String:name];// 通過(guò) KVC 進(jìn)行取值[coder encodeObject:[self valueForKey:key] forKey:key];} }@end

2.3 對(duì)多種類型的對(duì)象進(jìn)行歸檔

我們可以對(duì)多種類型的對(duì)象進(jìn)行歸檔,下面例子就是對(duì)基本類型變量、OC 對(duì)象,結(jié)構(gòu)體類型變量等進(jìn)行歸檔。

// 要進(jìn)行歸檔的對(duì)象 NSInteger num = 1024; NSString *name = @"Veggie"; CGPoint point = CGPointMake(1, -1);NSMutableData *mutibleData = [NSMutableData data]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:mutibleData];// 對(duì)要進(jìn)行歸檔的數(shù)據(jù)進(jìn)行編碼,轉(zhuǎn)化二進(jìn)制 [archiver encodeInteger:num forKey:@"num"]; [archiver encodeObject:name forKey:@"name"]; [archiver encodeCGPoint:point forKey:@"point"];[archiver finishEncoding];// 確定數(shù)據(jù)在沙盒中的存儲(chǔ)路徑 NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *dataPath = [docPath stringByAppendingPathComponent:@"data.archiver"];// 歸檔 BOOL isSussess = [mutibleData writeToFile:dataPath atomically:YES]; NSLog(@"%d", isSussess);

反歸檔并輸出結(jié)果進(jìn)行驗(yàn)證。

// 數(shù)據(jù)在沙盒中的存儲(chǔ)路徑 NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *dataPath = [docPath stringByAppendingPathComponent:@"data.archiver"];// 創(chuàng)建 NSData 和 NSKeyedUnarchiver 對(duì)象,反歸檔獲取二進(jìn)制數(shù)據(jù) NSData *data = [[NSData alloc] initWithContentsOfFile:dataPath]; NSKeyedUnarchiver *unArchhiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];// 解碼獲取數(shù)據(jù) NSInteger num = [unArchhiver decodeIntegerForKey:@"num"]; NSString *name = [unArchhiver decodeObjectForKey:@"name"]; CGPoint point = [unArchhiver decodeCGPointForKey:@"point"];// 解碼完成 [unArchhiver finishDecoding];// 輸出結(jié)果進(jìn)行驗(yàn)證 NSLog(@"num: %ld", (long)num); NSLog(@"name: %@", name); NSLog(@"point: (%.0f, %.0f)", point.x, point.y);

3. 解決方法已被棄用問(wèn)題

在編寫上面的歸檔代碼時(shí),你會(huì)發(fā)現(xiàn)方法 archiveRootObject:toFile: 和 unarchiveObjectWithFile: 等的一些方法在 iOS 12 中已經(jīng)被棄用,會(huì)收 Xcode 提示的 deprecated in iOS 12.0 的 warning。

雖然說(shuō)被棄用并不是能用,但是我們要編寫質(zhì)量更高的代碼,就要去學(xué)習(xí)新的 API,在新的 iOS 版本中使用新的系統(tǒng)方法,避免編譯器報(bào) warning。

3.1 舊方法被棄用的原因

以往 iOS 防止數(shù)據(jù)篡改,主要重心放在網(wǎng)絡(luò)傳輸策略,但是數(shù)據(jù)處理部分依然存在被攻擊風(fēng)險(xiǎn)。而歸檔,正是短板所在而所有對(duì)象歸檔、編解碼都會(huì)走同一個(gè)方法,這對(duì)攻擊者而言也十分方便。

于是,蘋果在 iOS 12 之后為歸檔方法提供了安全的編解碼方式,它主要是利用類名校驗(yàn)機(jī)制,再編解碼之前,先檢測(cè)數(shù)據(jù)是否為指定的類,然后再?zèng)Q定是否要編解碼

3.2 新方法的使用

在新的歸檔方法中,它要求被歸檔對(duì)象所屬的類遵循繼承自 NSCoding 的 NSSecureCoding 協(xié)議,它相比 NSCoding 協(xié)議需要額外實(shí)現(xiàn)如下方法,方法返回 true 表示支持安全的編解碼方式。

+ (BOOL)supportsSecureCoding {return YES; }

PS:Foundation 框架的常用類都有遵循該協(xié)議

這里是對(duì) 2.1 應(yīng)用實(shí)例的修改,運(yùn)用了新的歸檔方法。

NSArray *arr = @[@"Kobe", @"James", @"Iverson", @"Curry"];NSError *error = nil; // 新的歸檔方法 NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr requiringSecureCoding:YES error:&error]; if (data == nil && error != nil) {NSLog(@"歸檔失敗 %@", error); }// 在沙盒中獲取 Documents 路徑 NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; // 歸檔對(duì)象的最終存儲(chǔ)位置 NSString *arrPath = [docPath stringByAppendingPathComponent:@"arr.archiver"]; // 后綴名可隨意填寫[data writeToFile:arrPath atomically:YES];

相對(duì)應(yīng)地反歸檔操作,這里也運(yùn)用了新的反歸檔方法。

// 在沙盒中獲取 Documents 路徑 NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; // 歸檔對(duì)象的最終存儲(chǔ)位置 NSString *arrPath = [docPath stringByAppendingPathComponent:@"arr.archiver"]; // 后綴名可隨意填寫 NSData *data = [NSData dataWithContentsOfFile:arrPath];NSError *error = nil; // 新的反歸檔方法 NSArray *arr = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:data error:&error];if (arr == nil && error != nil) {NSLog(@"反歸檔失敗:%@", error); } else {// 遍歷獲取到的數(shù)組,驗(yàn)證結(jié)果[arr enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {NSLog(@"%lu - %@", idx, obj);}]; }

對(duì)于多種類型對(duì)象的歸檔,目前還有沒有找到像 2.3 那樣便捷的方法,感覺在 iOS 12 之后,歸檔能力有所下降。但是考慮到歸檔的應(yīng)用場(chǎng)景,所要進(jìn)行歸檔對(duì)象基本都是同一類型的,如果涉及到多種類型,可以選擇進(jìn)行多次歸檔操作,它所提供的 API 已經(jīng)能滿足我們平時(shí)使用的需求。

參考資料

  • https://www.jianshu.com/p/185e9ea021a2
  • https://www.jianshu.com/p/3e08fa21316d
  • https://github.com/ChenYilong/iOS12AdaptationTips/issues/1
  • https://stackoverflow.com/questions/51372551/how-to-replace-nskeyedarchivers-initializer-initforwritingwith-in-ios-12-to
  • NSKeyedUnarchiver | Apple Developer Documentation

總結(jié)

以上是生活随笔為你收集整理的iOS - 数据的归档和反归档的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。