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

文件加載與保存

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

1.屬性列表

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

1.1NSDate

  • 程序中經(jīng)常要處理時(shí)間和日期。NSDate類是Cocoa中用于處理日期和時(shí)間的基礎(chǔ)(Foundation)類。
  • 可以使用[NSDate data]來獲取當(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í)間間隔。通過該參數(shù)可以指定時(shí)間偏移的方式:對(duì)于將來的時(shí)間,使用時(shí)間間隔的整數(shù);對(duì)于過去的時(shí)間,使用時(shí)間間隔的負(fù)數(shù)。
  • 如果你想要設(shè)定輸出結(jié)果的時(shí)間格式,蘋果公司提供了一個(gè)叫做NSDateFormatter的類,它符合35號(hào)Unicode技術(shù)標(biāo)準(zhǔn),能為用戶提供多種時(shí)間的顯示格式。

1.2NSData

  • 將緩沖區(qū)(buffer)的數(shù)據(jù)傳遞給函數(shù)是C語言中常見的操作。通常是將緩沖區(qū)的指針和長(zhǎng)度傳遞給某個(gè)函數(shù)。另外,C語言中可能會(huì)出現(xiàn)內(nèi)存管理問題。例如,如果緩沖區(qū)已經(jīng)被動(dòng)態(tài)分配,那么當(dāng)它不在使用時(shí),由誰負(fù)責(zé)將其清除?
  • Cocoa提供了NSData類,該類可以包含大量字節(jié)。你可以獲取數(shù)據(jù)的長(zhǎng)度和指向字節(jié)和起始位置的指針。因?yàn)镹SData是一個(gè)對(duì)象,所以常規(guī)的內(nèi)存管理對(duì)它是有效的。因此,如果想將數(shù)據(jù)塊傳遞給一個(gè)函數(shù)或方法,可以通過傳遞一個(gè)支持自動(dòng)釋放的NSData來實(shí)現(xiàn),而無需擔(dān)心內(nèi)存清理的問題。下面的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表(打開終端,并鍵入命令man ascii就可以找到該表),就可以看到,這個(gè)十六進(jìn)制數(shù)據(jù)塊就是我們的字符串,0×48表示字符H,0×69表示字符i等。-length方法給出字節(jié)數(shù)量,-byte方法給出指向字符串起始位置的指針。注意到+dataWiithBytes:調(diào)用中的+1了嗎?它用于C語言字符串所需的尾部的零字節(jié)。還需注意NSlog輸出結(jié)果末尾的00。通過包含零字節(jié),就可以使用%s格式的說明符輸出字符串。
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)容。不過NSMutableData支持在數(shù)據(jù)內(nèi)容中添加和刪除字節(jié)。

1.3寫入和讀取屬性列表

  • 集合屬性列表類(NSArray和NSDictionary)具有一個(gè)-writeToFile:atomically:方法,用于將屬性列表的內(nèi)容寫入文件。
  • NSString和NSData也具有-writeToFile:atomically:方法,不過只能寫出字符串或者數(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ǔ)的。通過使用plutil命令:plutil -convert xml1文件名.plist,可以將這些文件轉(zhuǎn)化成人可以理解的字面形式。

  • 現(xià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ī)制:如果在保存過程中出現(xiàn)意外,不會(huì)破壞原始文件。但這種安全機(jī)制需要付出一定的代價(jià):在保存過程中,由于原始文件仍然保存在磁盤中,所以需要使用雙倍的磁盤空間。你應(yīng)該盡量使用atomically的方式保存文件,除非保存的文件容量非常大,會(huì)占用用戶大量的硬盤空間。
  • 如果能將數(shù)據(jù)歸結(jié)為屬性列表類型,則可以使用這些非常便捷的方法調(diào)用來將內(nèi)容保存到磁盤中,供以后讀取。
  • 這些函數(shù)的一個(gè)缺點(diǎn)是不會(huì)返回任何錯(cuò)誤信息。如果無法加載文件,你只能從方法中獲得一個(gè)nil指針,但無法得知出現(xiàn)錯(cuò)誤的具體原因。

1.4修改對(duì)象類型

  • 需要注意,當(dāng)使用集合類從某文件讀取數(shù)據(jù)時(shí),你無法修改數(shù)據(jù)的類型。一種解決方法是強(qiáng)制轉(zhuǎn)換,遍歷plist文件的內(nèi)容并創(chuàng)建一個(gè)平行結(jié)構(gòu)的可修改對(duì)象。不過還有一種方法,需要用到類NSPropertyListSerialization,正如名字所提示的,它可以為存儲(chǔ)和加載屬性列表的行為添加很多你需要的設(shè)定項(xiàng)。
  • 尤其要注意propertyListFromData:mutabilityOption:format:errorDescription:方法。它能把plist數(shù)據(jù)返回給你,并且能在出現(xiàn)異常的時(shí)候提供錯(cuò)誤信息。
  • 以下是將plist數(shù)據(jù)內(nèi)容以二進(jìn)制形式寫入文件的代碼:
NSString *error = nil;
    NSData *encodedArray = [NSPropertyListSerialization dataFromPropertyList:capitols format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error];
    [encodedArray writeToFile:@"/tmp/capitols.txt" atomically:YES];
  • 如你所見,我們將數(shù)組數(shù)據(jù)轉(zhuǎn)化成了NSData類型并寫入了文件中。
  • 將數(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)就是以什么方式來讀取數(shù)據(jù):我們是否想要能夠修改plist文件的類型?我們是想獲取列表結(jié)構(gòu)還是僅獲取二進(jìn)制數(shù)據(jù)?
  • 以下是將屬性列表文件plist數(shù)據(jù)內(nèi)容以二進(jìn)制形式寫入文件的代碼:
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];

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

  • 將數(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]來獲取當(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ǔ)的。通過使用plutil命令:plutil - convert xml1文件名.plist,可以將這些文件轉(zhuǎn)換成人可以理解的字面形式。

2.編碼對(duì)象

  • 不幸的是,你無法總是將對(duì)象信息表示為屬性列表類。
  • Cocoa具備一種將對(duì)象轉(zhuǎn)換成某種格式并保存到磁盤中的機(jī)制。對(duì)象可以將它們的實(shí)例變量和其他數(shù)據(jù)編碼為數(shù)據(jù)塊,然后保存到磁盤中。這些數(shù)據(jù)塊以后還可以讀回內(nèi)存中,并且還能基于保存的數(shù)據(jù)創(chuàng)建新對(duì)象。這個(gè)過程被稱為編碼與解碼(encoding and decoding),也可以叫做序列化與反序列化(serialization and deserialization)。
  • 前面說Interface Builder時(shí),我們從庫中將對(duì)象拖到窗口,這些對(duì)象會(huì)被存儲(chǔ)到nib文件中。換句話說,NSWindow和NSTextField對(duì)象都被序列化并保存到磁盤中。當(dāng)程序運(yùn)行時(shí),會(huì)將nib文件加載到內(nèi)存中,對(duì)象會(huì)被反序列化,新的NSWindow與NSTextField對(duì)象會(huì)被創(chuàng)建并建立關(guān)系。
  • 通過采用NSCoding協(xié)議,可以使自己的對(duì)象實(shí)現(xiàn)相同的功能。該協(xié)議與下面的代碼類似。
@protocol NSCoding
- (void) encodeWithCoder:(NSCoder *) encoder;
- (id) initWithCoder:(NSCoder *) decoder;
@end
  • 通過采用該協(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)換,你完全沒必要?jiǎng)?chuàng)建一個(gè)新的NSCoder對(duì)象,因?yàn)閷?shí)際上并不會(huì)起作用。不過有一些具體實(shí)現(xiàn)的NSCoder子類可以用來編碼和解碼對(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)在,我們來將這個(gè)對(duì)象歸檔
//我們使用NSKeyedArchiver把對(duì)象歸檔到NSData中,KeyedArchiver使用鍵/值來保存對(duì)象的信息
//Thingie的encodeWithCoder方法會(huì)使用與每個(gè)實(shí)例變量名稱匹配的鍵對(duì)其進(jìn)行編碼
//也可以使用無規(guī)則的文字作為鍵來編碼,保證鍵的名稱與實(shí)例變量的名稱相似有助于識(shí)別它們之間的映射關(guān)系
//可以使用上面的字面量字符串作為編碼建
//可以使用#define kSubthingiesKey @"subThingies"來定義常量
//可以使用文件的局部變量,比如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)行了編碼和解碼操作,沒有對(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使用鍵/值來保存對(duì)象的信息。

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

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

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

  • 可以使用#define kSubthingiesKey @"subThingies"來定義常量。

  • 可以使用文件的局部變量,比如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)行了編碼和解碼操作,沒有對(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è)無限的遞歸,試圖創(chuàng)建日志信息,最終會(huì)導(dǎo)致成千上萬的-description調(diào)用進(jìn)入調(diào)試器中。不過,如果對(duì)thing1進(jìn)行編碼和解碼,它可以完美的運(yùn)行,也不會(huì)陷入混亂中。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,577評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,600評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,944評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,108評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,652評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,385評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,616評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,798評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評(píng)論 1 295
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,334評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,570評(píng)論 2 379

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