文件加載與保存
- 大多數(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ì)陷入混亂中。