第十七章邪码、文件加載與保存

文件加載與保存

  • 大多數(shù)計(jì)算機(jī)程序(應(yīng)用程序)在關(guān)閉時(shí)都會(huì)為用戶的當(dāng)前成果創(chuàng)建一個(gè)臨時(shí)的(非永久的)文件是复,可能是小說(shuō)的某個(gè)章節(jié)删顶,或是某個(gè)樂(lè)隊(duì)專輯的封面。但無(wú)論是那種情形淑廊,用戶都會(huì)有個(gè)文件保存在磁盤(pán)上逗余。
  • 標(biāo)準(zhǔn)C函數(shù)庫(kù)提供了函數(shù)調(diào)用來(lái)創(chuàng)建,讀取和寫(xiě)入文件季惩,例如open(),read(),write(),fopen()和fread()录粱。而Cocoa提供了Core Data,它能在后臺(tái)進(jìn)行文件的以上所有操作画拾。
  • Cocoa提供了兩個(gè)處理文件的通用類:屬性對(duì)象和對(duì)象編碼啥繁。

1.屬性列表

在Cocoa中,有一類名為屬性列表(property list)的對(duì)象青抛,通常簡(jiǎn)寫(xiě)為plist旗闽。這些屬性列表類是NSArray,NSDictionary蜜另,NSString适室,NSNumber,NSDate和NSData(前面四個(gè)已經(jīng)講過(guò))举瑰,以及它們的可修改形態(tài)(只要它們能擁有前綴為Mutable的類)

1.1NSDate

  • 程序中經(jīng)常要處理時(shí)間和日期捣辆。NSDate類是Cocoa中用于處理日期和時(shí)間的基礎(chǔ)(Foundation)類。
  • 可以使用[NSDate data]來(lái)獲取當(dāng)前的日期和時(shí)間此迅,它會(huì)返回一個(gè)能自動(dòng)釋放的對(duì)象汽畴。
NSDate *date = [NSDate date];
NSLog(@"today is %@",date);
//輸出結(jié)果
today is 2018-12-01 20:21:02 -0400
  • 你可以使用一些方法比較兩個(gè)日期,從而對(duì)列表進(jìn)行排序邮屁,還可以獲取與當(dāng)前時(shí)間間隔一定時(shí)差的日期整袁。
//獲取24小時(shí)之前的確切時(shí)間
NSDate *yesterday = [NSDate dateWithTimeIntervalSinceNow:-(24*60*60)];
NSLog(@"yesterday is %@",yesterday);
//輸出結(jié)果
yesterday is 2018-11-30 20:21:02 -0400
  • +dateWithTimeIntervalSinceNow:接受一個(gè)NSTimeInterval參數(shù),該參數(shù)是一個(gè)雙精度值佑吝,表示以妙為單位計(jì)算的時(shí)間間隔坐昙。通過(guò)該參數(shù)可以指定時(shí)間偏移的方式:對(duì)于將來(lái)的時(shí)間,使用時(shí)間間隔的整數(shù)芋忿;對(duì)于過(guò)去的時(shí)間炸客,使用時(shí)間間隔的負(fù)數(shù)疾棵。
  • 如果你想要設(shè)定輸出結(jié)果的時(shí)間格式,蘋(píng)果公司提供了一個(gè)叫做NSDateFormatter的類痹仙,它符合35號(hào)Unicode技術(shù)標(biāo)準(zhǔn)是尔,能為用戶提供多種時(shí)間的顯示格式。

1.2NSData

  • 將緩沖區(qū)(buffer)的數(shù)據(jù)傳遞給函數(shù)是C語(yǔ)言中常見(jiàn)的操作开仰。通常是將緩沖區(qū)的指針和長(zhǎng)度傳遞給某個(gè)函數(shù)拟枚。另外,C語(yǔ)言中可能會(huì)出現(xiàn)內(nèi)存管理問(wèn)題众弓。例如恩溅,如果緩沖區(qū)已經(jīng)被動(dòng)態(tài)分配,那么當(dāng)它不在使用時(shí)谓娃,由誰(shuí)負(fù)責(zé)將其清除脚乡?
  • Cocoa提供了NSData類,該類可以包含大量字節(jié)滨达。你可以獲取數(shù)據(jù)的長(zhǎng)度和指向字節(jié)和起始位置的指針奶稠。因?yàn)镹SData是一個(gè)對(duì)象,所以常規(guī)的內(nèi)存管理對(duì)它是有效的捡遍。因此锌订,如果想將數(shù)據(jù)塊傳遞給一個(gè)函數(shù)或方法,可以通過(guò)傳遞一個(gè)支持自動(dòng)釋放的NSData來(lái)實(shí)現(xiàn)稽莉,而無(wú)需擔(dān)心內(nèi)存清理的問(wèn)題瀑志。下面的NSData對(duì)象將保存一個(gè)普通的C字符串(一個(gè)字節(jié)序列),然后輸出數(shù)據(jù)污秆。
const char *string = "Hi there,this is a C string!";
NSData *data = [NSData dataWithBytes: length: strlen(string) + 1];
NSLog(@"data is %@",data);
//運(yùn)行結(jié)果
data is <48692074 68657265 2c207468 69732069 73206120 43207374 72696e67 2100>
  • 上面的輸出有點(diǎn)特別劈猪,但是如果你有ASCII表(打開(kāi)終端,并鍵入命令man ascii就可以找到該表)良拼,就可以看到战得,這個(gè)十六進(jìn)制數(shù)據(jù)塊就是我們的字符串,0×48表示字符H庸推,0×69表示字符i等常侦。-length方法給出字節(jié)數(shù)量,-byte方法給出指向字符串起始位置的指針贬媒。注意到+dataWiithBytes:調(diào)用中的+1了嗎?它用于C語(yǔ)言字符串所需的尾部的零字節(jié)聋亡。還需注意NSlog輸出結(jié)果末尾的00。通過(guò)包含零字節(jié)际乘,就可以使用%s格式的說(shuō)明符輸出字符串坡倔。
NSLog(@"%d byte string is "%s",[data length],[data bytes]);
//輸出結(jié)果
30 byte is "Hi there,this is a C string!"
  • NSData對(duì)象是不可變更的,創(chuàng)建后就不可以改變罪塔。你可以使用它們投蝉,但不能更改其中的內(nèi)容。不過(guò)NSMutableData支持在數(shù)據(jù)內(nèi)容中添加和刪除字節(jié)征堪。

1.3寫(xiě)入和讀取屬性列表

  • 集合屬性列表類(NSArray和NSDictionary)具有一個(gè)-writeToFile:atomically:方法瘩缆,用于將屬性列表的內(nèi)容寫(xiě)入文件。
  • NSString和NSData也具有-writeToFile:atomically:方法佃蚜,不過(guò)只能寫(xiě)出字符串或者數(shù)據(jù)塊庸娱。因此,我們可以將字符串存入一個(gè)數(shù)組爽锥,然后保存它涌韩。

相關(guān)例子代碼:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    NSArray *phrase;
    phrase = [NSArray arrayWithObjects:@"I",@"seem",@"to",@"be",@"a",@"verb",nil];
    [phrase writeToFile:@"/Users/xulei/Desktop/file.txt" atomically:YES];
    return 0;
}

運(yùn)行結(jié)果:

  • 以上代碼雖然有些繁瑣畔柔,但正是我們要保存的內(nèi)容:一個(gè)字符串?dāng)?shù)組氯夷。如果保存的數(shù)組是包含了各種字符串?dāng)?shù)組,數(shù)字和數(shù)據(jù)的字典靶擦,那么這些屬性列表文件的內(nèi)容將相當(dāng)復(fù)雜腮考。Xcode也包含了一個(gè)屬性列表編輯器,所以你可以查看plist文件并進(jìn)行編輯玄捕。

  • 有些屬性列表文件踩蔚,特別是首選項(xiàng)文件,是以壓縮的二進(jìn)制格式存儲(chǔ)的枚粘。通過(guò)使用plutil命令:plutil -convert xml1文件名.plist馅闽,可以將這些文件轉(zhuǎn)化成人可以理解的字面形式。

  • 現(xiàn)在我們的磁盤(pán)中已經(jīng)有了verbiage.txt文件馍迄,可以使用+arrayWithContentsOfFile:方法讀取該文件福也。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    NSArray *phrase1;
    phrase1 = [NSArray arrayWithObjects:@"I",@"seem",@"to",@"be",@"a",@"verb",nil];
    [phrase1 writeToFile:@"/Users/xulei/Desktop/file.txt" atomically:YES];
    NSArray *phrase2 = [NSArray arrayWithContentsOfFile:@"/Users/xulei/Desktop/file.txt"];
    NSLog(@"%@",phrase2);
    return 0;
}
//運(yùn)行結(jié)果
2018-12-18 12:47:02.350581-0800 OC文件[632:19018] (
    I,
    seem,
    to,
    be,
    a,
    verb
)
Program ended with exit code: 0
  • 注意到writeToFile:方法中的atomically了嗎?atomically:參數(shù)的值為BOOL類型攀圈,它會(huì)告訴Cocoa是否應(yīng)該首先將文件內(nèi)容保存在臨時(shí)文件中暴凑,再將該臨時(shí)文件和原始文件交換。
  • 這是一種安全機(jī)制:如果在保存過(guò)程中出現(xiàn)意外赘来,不會(huì)破壞原始文件现喳。但這種安全機(jī)制需要付出一定的代價(jià):在保存過(guò)程中,由于原始文件仍然保存在磁盤(pán)中犬辰,所以需要使用雙倍的磁盤(pán)空間嗦篱。你應(yīng)該盡量使用atomically的方式保存文件,除非保存的文件容量非常大幌缝,會(huì)占用用戶大量的硬盤(pán)空間灸促。
  • 如果能將數(shù)據(jù)歸結(jié)為屬性列表類型,則可以使用這些非常便捷的方法調(diào)用來(lái)將內(nèi)容保存到磁盤(pán)中,供以后讀取腿宰。
  • 這些函數(shù)的一個(gè)缺點(diǎn)是不會(huì)返回任何錯(cuò)誤信息呕诉。如果無(wú)法加載文件,你只能從方法中獲得一個(gè)nil指針吃度,但無(wú)法得知出現(xiàn)錯(cuò)誤的具體原因甩挫。

1.4修改對(duì)象類型

  • 需要注意,當(dāng)使用集合類從某文件讀取數(shù)據(jù)時(shí)椿每,你無(wú)法修改數(shù)據(jù)的類型伊者。一種解決方法是強(qiáng)制轉(zhuǎn)換,遍歷plist文件的內(nèi)容并創(chuàng)建一個(gè)平行結(jié)構(gòu)的可修改對(duì)象间护。不過(guò)還有一種方法亦渗,需要用到類NSPropertyListSerialization,正如名字所提示的汁尺,它可以為存儲(chǔ)和加載屬性列表的行為添加很多你需要的設(shè)定項(xiàng)法精。
  • 尤其要注意propertyListFromData:mutabilityOption:format:errorDescription:方法。它能把plist數(shù)據(jù)返回給你痴突,并且能在出現(xiàn)異常的時(shí)候提供錯(cuò)誤信息搂蜓。
  • 以下是將plist數(shù)據(jù)內(nèi)容以二進(jìn)制形式寫(xiě)入文件的代碼:
NSString *error = nil;
    NSData *encodedArray = [NSPropertyListSerialization dataFromPropertyList:capitols format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error];
    [encodedArray writeToFile:@"/tmp/capitols.txt" atomically:YES];
  • 如你所見(jiàn),我們將數(shù)組數(shù)據(jù)轉(zhuǎn)化成了NSData類型并寫(xiě)入了文件中辽装。
  • 將數(shù)據(jù)讀取回內(nèi)存要多執(zhí)行一步帮碰,即指定文件的類型。我們創(chuàng)建一個(gè)指針拾积,如果文件格式與指定的類型不同,可以換用原格式類型的指針拓巧,也可以將讀取的內(nèi)容轉(zhuǎn)化成信的格式。
 NSPropertyListFormat propertyListFormat = NSPropertyListXMLFormat_v1_0;
    NSString *error = nil;
    NSMutableArray *capitols = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainersAndLeaves format:&propertyListFormat error:&error];
  • 其中一個(gè)選項(xiàng)就是以什么方式來(lái)讀取數(shù)據(jù):我們是否想要能夠修改plist文件的類型输拇?我們是想獲取列表結(jié)構(gòu)還是僅獲取二進(jìn)制數(shù)據(jù)?
  • 以下是將屬性列表文件plist數(shù)據(jù)內(nèi)容以二進(jìn)制形式寫(xiě)入文件的代碼:
void writeCapitols (void)

{

    NSMutableArray *capitols = [NSMutableArrayarrayWithCapacity:10];

    NSMutableDictionary *capitol = [NSMutableDictionarydictionaryWithObject:@"Canada"forKey:@"country"];

    [capitol setObject:@"Ottawa"forKey:@"capitol"];

    [capitols addObject:capitol];

    capitol = [NSMutableDictionarydictionaryWithObject:@"Norway"forKey:@"country"];

    [capitol setObject:@"Oslo"forKey:@"capitol"];

    [capitols addObject:capitol];

    

    NSString *error =nil;

    NSData *encodedArray = [NSPropertyListSerializationdataFromPropertyList:capitols 

                                                                     format:NSPropertyListBinaryFormat_v1_0 

                                                           errorDescription:&error];

    [encodedArray writeToFile:@"/tmp/capitols.txt"atomically:YES];

}
  • 如你所見(jiàn)贤斜,我們將數(shù)組數(shù)據(jù)轉(zhuǎn)換成了NSData類型并寫(xiě)入了文件中。

  • 將數(shù)據(jù)讀取回內(nèi)存要多執(zhí)行一步瘩绒,即指定文件的類型猴抹。我們創(chuàng)建了一個(gè)指針,如果文件格式與指定的類型不同锁荔,可以換用原格式類型的指針,也可以將讀取的內(nèi)容轉(zhuǎn)換成新的格式跋理。

static void modifyCapitols(void)

{

    NSData *data = [NSDatadataWithContentsOfFile:@"/tmp/capitols.txt"];

    NSPropertyListFormat propertyListFormat =NSPropertyListXMLFormat_v1_0;

    NSString *error =nil;

    NSMutableArray *capitols = [NSPropertyListSerializationpropertyListFromData:data

                                                               mutabilityOption:NSPropertyListMutableContainersAndLeaves

                                                                         format:&propertyListFormat

                                                               errorDescription:&error];

    NSLog(@"capitols %@", capitols);

}
  • 在main函數(shù)中前普,我們調(diào)用writeCapitols();和modifyCapitols();的輸出結(jié)果如下:
capitols (

        {

        capitol = Ottawa;

        country = Canada;

    },

        {

        capitol = Oslo;

        country = Norway;

    }

)

  • 可以使用[NSDate date]來(lái)獲取當(dāng)前的日期和時(shí)間,它會(huì)返回一個(gè)能自動(dòng)釋放的對(duì)象拭卿。
NSDate *date = [NSDatedate];

NSLog (@"today is %@", date);

將輸出的結(jié)果為:
today is 2013-12-4 19:58:06 -0400峻厚。

  • 有些屬性列表文件,特別是首選項(xiàng)文件浦夷,是以壓縮的二進(jìn)制格式存儲(chǔ)的刽射。通過(guò)使用plutil命令:plutil - convert xml1文件名.plist誓禁,可以將這些文件轉(zhuǎn)換成人可以理解的字面形式肾档。

2.編碼對(duì)象

  • 不幸的是,你無(wú)法總是將對(duì)象信息表示為屬性列表類俗慈。
  • Cocoa具備一種將對(duì)象轉(zhuǎn)換成某種格式并保存到磁盤(pán)中的機(jī)制遣耍。對(duì)象可以將它們的實(shí)例變量和其他數(shù)據(jù)編碼為數(shù)據(jù)塊,然后保存到磁盤(pán)中酣溃。這些數(shù)據(jù)塊以后還可以讀回內(nèi)存中纪隙,并且還能基于保存的數(shù)據(jù)創(chuàng)建新對(duì)象绵咱。這個(gè)過(guò)程被稱為編碼與解碼(encoding and decoding),也可以叫做序列化與反序列化(serialization and deserialization)艾恼。
  • 前面說(shuō)Interface Builder時(shí),我們從庫(kù)中將對(duì)象拖到窗口秆吵,這些對(duì)象會(huì)被存儲(chǔ)到nib文件中五慈。換句話說(shuō)泻拦,NSWindow和NSTextField對(duì)象都被序列化并保存到磁盤(pán)中。當(dāng)程序運(yùn)行時(shí)腋粥,會(huì)將nib文件加載到內(nèi)存中架曹,對(duì)象會(huì)被反序列化绑雄,新的NSWindow與NSTextField對(duì)象會(huì)被創(chuàng)建并建立關(guān)系。
  • 通過(guò)采用NSCoding協(xié)議罗珍,可以使自己的對(duì)象實(shí)現(xiàn)相同的功能脚粟。該協(xié)議與下面的代碼類似核无。
@protocol NSCoding
- (void) encodeWithCoder:(NSCoder *) encoder;
- (id) initWithCoder:(NSCoder *) decoder;
@end
  • 通過(guò)采用該協(xié)議,可以實(shí)現(xiàn)兩種方法噪沙。當(dāng)對(duì)象需要保存自身時(shí)已慢,就會(huì)調(diào)用-encodeWithCoder:方法佑惠;當(dāng)對(duì)象需要加載自身時(shí)齐疙,就會(huì)調(diào)用-initWithCoder:方法贞奋。
  • NSCoder是一個(gè)抽象類穷绵,它定義了一些有用的方法仲墨,便于對(duì)象與NSData之間的轉(zhuǎn)換,你完全沒(méi)必要?jiǎng)?chuàng)建一個(gè)新的NSCoder對(duì)象俩由,因?yàn)閷?shí)際上并不會(huì)起作用癌蚁。不過(guò)有一些具體實(shí)現(xiàn)的NSCoder子類可以用來(lái)編碼和解碼對(duì)象努释,我們使用其中的兩個(gè)子類NSKeyedArchiver和NSKeyedUnarchiver。
#import <Foundation/Foundation.h>

//包含一些實(shí)例變量的簡(jiǎn)單類
@interface Thingie : NSObject <NSCoding>{
    NSString *name;
    int magicNumber;
    float shoeSize;
    NSMutableArray *subThingies;
}
@property (copy) NSString *name;
@property  int magicNumber;
@property  float shoeSize;
@property (retain) NSMutableArray *subThingies;
-(id)initWithName:(NSString *) n magicNumber:(int) mn shoeSize:(float) ss;
@end

//Thingie類采用了NSCoding協(xié)議這意味著我們將要實(shí)現(xiàn)encodeWithCoder:方法和initWithCoder:方法
//先將這兩個(gè)方法的內(nèi)容置空
//該代碼初始化了一個(gè)新的對(duì)象煞躬,清除了我們創(chuàng)建的所有東西汰翠,并且為NSCodering協(xié)議的方法創(chuàng)建了結(jié)構(gòu)框架以避免編譯器報(bào)錯(cuò)昭雌,最后返回了描述信息的方法健田。
@implementation Thingie
@synthesize name;
@synthesize magicNumber;
@synthesize shoeSize;
@synthesize subThingies;
- (id)initWithName:(NSString *)n magicNumber:(int)mn shoeSize:(float)ss{
    if (self = [super init]) {
        self.name = n;
        self.magicNumber = mn;
        self.shoeSize = ss;
        self.subThingies = [NSMutableArray array];
    }
    return self;
}
-(void)dealloc{
    
}
//現(xiàn)在妓局,我們來(lái)將這個(gè)對(duì)象歸檔
//我們使用NSKeyedArchiver把對(duì)象歸檔到NSData中好爬,KeyedArchiver使用鍵/值來(lái)保存對(duì)象的信息
//Thingie的encodeWithCoder方法會(huì)使用與每個(gè)實(shí)例變量名稱匹配的鍵對(duì)其進(jìn)行編碼
//也可以使用無(wú)規(guī)則的文字作為鍵來(lái)編碼,保證鍵的名稱與實(shí)例變量的名稱相似有助于識(shí)別它們之間的映射關(guān)系
//可以使用上面的字面量字符串作為編碼建
//可以使用#define kSubthingiesKey @"subThingies"來(lái)定義常量
//可以使用文件的局部變量炬搭,比如static NSString *kSubthingiesKey = @"subThingies";所定義的靜態(tài)變量
//注意,每種類型的encodeSomething:forKey:方法都不同融虽。需要確保你的類型使用的是正確的編碼方法灼芭。
//對(duì)于所有的Objective-C對(duì)象類型彼绷,都要使用encodeObjective:forKey:方法。
-(void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:name forKey:@"name"];
    [aCoder encodeInt:magicNumber forKey:@"magicNumber"];
    [aCoder encodeFloat:shoeSize forKey:@"shoeSize"];
    [aCoder encodeObject:subThingies forKey:@"subThingies"];
}
//如果需要恢復(fù)某個(gè)歸檔的對(duì)象萤衰,可以使用decodeSomethingForKey方法
//initWithCoder:和其它的init:方法一樣腻菇,在為對(duì)象執(zhí)行操作之前昔馋,需要使用超類進(jìn)行初始化
//可以采用兩種方式秘遏,具體取決于父類
//如果父類采用了NSCoding協(xié)議,則應(yīng)該調(diào)用[super initWithCoder:aDecoder],否則只需要調(diào)用[super init]
//當(dāng)你使用decodeIntForKey:方法時(shí)洋侨,會(huì)把一個(gè)init值從aDecoder中取出
//當(dāng)你使用decodeObjectForKey:方法時(shí)倦蚪,會(huì)把一個(gè)對(duì)象從aDecoder中取出
//如果里面還有嵌入的對(duì)象陵且,就會(huì)對(duì)其遞歸調(diào)用initWithCoder:方法。
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self =[super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.magicNumber = [aDecoder decodeIntForKey:@"magicNumber"];
        self.shoeSize = [aDecoder decodeFloatForKey:@"shoeSize"];
        self.subThingies = [aDecoder decodeObjectForKey:@"subThingies"];
    }
    return self;
}
-(NSString *)description{
    NSString *description = [NSString stringWithFormat:@"%@: %d/%.1f %@",name,magicNumber,shoeSize,subThingies];
    return description;
}
@end
//你將會(huì)注意到聊疲,編碼和解碼的順序和示例對(duì)象的順序完全相同获洲。當(dāng)然殿如,你也可以不這么做,這只是一種簡(jiǎn)便的習(xí)慣
//目的在于確保每個(gè)對(duì)象都進(jìn)行了編碼和解碼操作飞崖,沒(méi)有對(duì)象被漏掉
//使用鍵進(jìn)行調(diào)節(jié)的原因之一就是可以將示例對(duì)象按任意順序放入或取出
int main(int argc, const char * argv[]) {
    Thingie *thing;
    thing = [[Thingie alloc] initWithName:@"thing1" magicNumber:42 shoeSize:10.5];
    NSLog(@"some thing:%@",thing);
    //使用thing1對(duì)象固歪,并將其歸檔
    NSData *freezeDried;
    //類方法+archivedDataWithRootObject:對(duì)這個(gè)對(duì)象進(jìn)行了編碼牢裳,比如字符串、數(shù)組及放入數(shù)組中的任何對(duì)象
    //當(dāng)所有的對(duì)象完成了鍵值編碼后忘朝,會(huì)被放入一個(gè)NSData并返回
    freezeDried = [NSKeyedArchiver archivedDataWithRootObject:thing];
    Thingie *thing1;
    thing1 = [NSKeyedUnarchiver unarchiveObjectWithData:freezeDried];
    NSLog(@"%@",thing1);
    //我們可以將對(duì)象放到subThing的可變數(shù)組中判帮,當(dāng)數(shù)組被編碼時(shí)晦墙,這些對(duì)象將被自動(dòng)編碼
    //NSArray中encodeWithCoder:方法的實(shí)現(xiàn)會(huì)對(duì)所有的對(duì)象調(diào)用encodeWithCoder方法,直到所有對(duì)象都被編碼
    Thingie *anotherThing;
    anotherThing = [[Thingie alloc] initWithName:@"thing2" magicNumber:23 shoeSize:13.0];
    [thing1.subThingies addObject:anotherThing];
    anotherThing = [[Thingie alloc] initWithName:@"thing3" magicNumber:17 shoeSize:9.0];
    [thing1.subThingies addObject:anotherThing];
    NSLog(@"%@",thing1);
    //編碼解碼的工作機(jī)制是一樣的
    freezeDried = [NSKeyedArchiver archivedDataWithRootObject:thing1];
    thing1 = [NSKeyedUnarchiver unarchiveObjectWithData:freezeDried];
    NSLog(@"%@",thing1);
    return 0;
}
//運(yùn)行結(jié)果
2018-12-21 06:26:32.998091-0800 編碼對(duì)象[1126:102536] some thing:thing1: 42/10.5 (
)
2018-12-21 06:26:32.999011-0800 編碼對(duì)象[1126:102536] thing1: 42/10.5 (
)
2018-12-21 06:26:32.999227-0800 編碼對(duì)象[1126:102536] thing1: 42/10.5 (
    "thing2: 23/13.0 (\n)",
    "thing3: 17/9.0 (\n)"
)
2018-12-21 06:26:32.999503-0800 編碼對(duì)象[1126:102536] thing1: 42/10.5 (
    "thing2: 23/13.0 (\n)",
    "thing3: 17/9.0 (\n)"
)
Program ended with exit code: 0
  • 我們使用NSKeyedArchiver把對(duì)象歸檔到NSData中,KeyedArchiver使用鍵/值來(lái)保存對(duì)象的信息抗楔。

  • Thingie的encodeWithCoder方法會(huì)使用與每個(gè)實(shí)例變量名稱匹配的鍵對(duì)其進(jìn)行編碼连躏。

  • 也可以使用無(wú)規(guī)則的文字作為鍵來(lái)編碼,保證鍵的名稱與實(shí)例變量的名稱相似有助于識(shí)別它們之間的映射關(guān)系卢肃。

  • 可以使用上面的字面量字符串作為編碼建。

  • 可以使用#define kSubthingiesKey @"subThingies"來(lái)定義常量郑气。

  • 可以使用文件的局部變量腰池,比如static NSString *kSubthingiesKey = @"subThingies";所定義的靜態(tài)變量忙芒。

  • 注意呵萨,每種類型的encodeSomething:forKey:方法都不同跨跨。需要確保你的類型使用的是正確的編碼方法勇婴。

  • 對(duì)于所有的Objective-C對(duì)象類型,都要使用encodeObjective:forKey:方法拘悦。

  • 如果需要恢復(fù)某個(gè)歸檔的對(duì)象础米,可以使用decodeSomethingForKey方法
    initWithCoder:和其它的init:方法一樣添诉,在為對(duì)象執(zhí)行操作之前吻商,需要使用超類進(jìn)行初始化。

  • 可以采用兩種方式乌叶,具體取決于父類.如果父類采用了NSCoding協(xié)議柒爸,則應(yīng)該調(diào)用[super initWithCoder:aDecoder],否則只需要調(diào)用[super init]捎稚。

  • 當(dāng)你使用decodeIntForKey:方法時(shí),會(huì)把一個(gè)init值從aDecoder中取出;當(dāng)你使用decodeObjectForKey:方法時(shí)葡公,會(huì)把一個(gè)對(duì)象從aDecoder中取出,如果里面還有嵌入的對(duì)象催什,就會(huì)對(duì)其遞歸調(diào)用initWithCoder:方法宰睡。

  • 你將會(huì)注意到,編碼和解碼的順序和示例對(duì)象的順序完全相同旋圆。當(dāng)然灵巧,你也可以不這么做,這只是一種簡(jiǎn)便的習(xí)慣艾君。目的在于確保每個(gè)對(duì)象都進(jìn)行了編碼和解碼操作冰垄,沒(méi)有對(duì)象被漏掉权她。使用鍵進(jìn)行調(diào)節(jié)的原因之一就是可以將示例對(duì)象按任意順序放入或取出隅要。

  • 如果編碼的數(shù)據(jù)中含有循環(huán)會(huì)怎么樣?例如thing1就在自己的subThingies數(shù)組中會(huì)怎么樣要门?會(huì)不停的重復(fù)編碼嗎欢搜?Cocoa的歸檔實(shí)現(xiàn)非常智能谴轮,對(duì)象循環(huán)也可以進(jìn)行保存或恢復(fù)第步。

  • 如果要進(jìn)行實(shí)驗(yàn)的話,可以將thing1放入其自身的subThingies數(shù)組中廓推。但是不要嘗試在thing1中使用NSLog翩隧,NSLog不夠智能,不能檢測(cè)對(duì)象循環(huán)。因此顽频,它將會(huì)陷入一個(gè)無(wú)限的遞歸糯景,試圖創(chuàng)建日志信息蟀淮,最終會(huì)導(dǎo)致成千上萬(wàn)的-description調(diào)用進(jìn)入調(diào)試器中。不過(guò)涨缚,如果對(duì)thing1進(jìn)行編碼和解碼脓魏,它可以完美的運(yùn)行通惫,也不會(huì)陷入混亂中。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市遵湖,隨后出現(xiàn)的幾起案子奄侠,更是在濱河造成了極大的恐慌垄潮,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旅急,死亡現(xiàn)場(chǎng)離奇詭異藐吮,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)迫摔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)句占,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)纱烘,“玉大人祈餐,你說(shuō)我怎么就攤上這事帆阳。” “怎么了变骡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵塌碌,是天一觀的道長(zhǎng)台妆。 經(jīng)常有香客問(wèn)我胖翰,道長(zhǎng)萨咳,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮俊扳,結(jié)果婚禮上猛遍,老公的妹妹穿的比我還像新娘。我一直安慰自己宽堆,他們只是感情好冤馏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布逮光。 她就那樣靜靜地躺著涕刚,像睡著了一般乙帮。 火紅的嫁衣襯著肌膚如雪察净。 梳的紋絲不亂的頭發(fā)上氢卡,一...
    開(kāi)封第一講書(shū)人閱讀 51,488評(píng)論 1 302
  • 那天译秦,我揣著相機(jī)與錄音,去河邊找鬼们拙。 笑死砚婆,一個(gè)胖子當(dāng)著我的面吹牛突勇,可吹牛的內(nèi)容都是我干的与境。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了党窜?” 一聲冷哼從身側(cè)響起幌衣,我...
    開(kāi)封第一講書(shū)人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤豁护,失蹤者是張志新(化名)和其女友劉穎欲间,沒(méi)想到半個(gè)月后猎贴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體她渴,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡趁耗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年对粪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了右冻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡著拭,死狀恐怖纱扭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情儡遮,我是刑警寧澤乳蛾,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站鄙币,受9級(jí)特大地震影響肃叶,放射性物質(zhì)發(fā)生泄漏十嘿。R本人自食惡果不足惜因惭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绩衷。 院中可真熱鬧蹦魔,春花似錦激率、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至低缩,卻和暖如春嘉冒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咆繁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工讳推, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人玩般。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓娜遵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親壤短。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容