第十三章 物件的複製及儲存

陳振發表於2017-12-13

物件的複製

淺複製和深複製

用與一個例項物件相同的內容,生成一個新物件,這個過程一般稱為複製。

只複製物件的指標稱為淺複製(shallow copy),而複製具有新的記憶體空間的物件則稱為深複製(deep copy)。

用指標共享某一物件的時候,同時也會共享那個物件的操作結果。而生成副本時,對原始檔和副本的操作都是獨立的。

區域

動態分配的記憶體管理稱為區域。新執行時系統不使用區域。

因為區域在執行效率上並沒有多大貢獻,現在較新的執行時環境中都沒有用到它。

通常生成例項物件要使用類方法alloc,而NSObject中也有一個類方法,可以指定區域來生成例項。

+ (id)allocWithZone:(NSZone)zone;

NSZone結構體是專門用於表達區域的資料結構。現代執行時中,zone一般設定為NULL。但方法的功能和alloc一樣。

複製方法的定義

NSObject中有copy方法,它能夠透過複製接收器來生成新例項。但是實際的複製操作並不是由copy來完成的,而是由例項方法copyWithZone:完成的。

鑑於這樣的複製方式,光實現copy是不行的,還需要定義方法copyWithZone:,該方法返回複製生成的新物件。

由於方法copyWithZone:是在協議NSCopying中宣告的,因此要在類中實現採用該協議的方法。如果該方法適用於協議NSCopying,那麼在方法copy的宣告屬性指定為可選等情況下,編譯器就會被告知可以進行復制。

@protocol

NSCopying

- (

id)copyWithZone:(NSZone*)zone;@end

方法copyWithZone:的定義方法可總結如下:

超類如果沒有實現方法copyWithZone:,可使用alloc和init...來生成新例項,並將該例項變數謹慎的複製並帶入。

超類如果實現了方法copyWithZone:,可以直接呼叫該方法來生成例項,再將子類中新增的例項變數根據需要複製並帶入。

例項變數中共享物件沒有必要複製。採用手動引用計數管理時需要retaion。

採用引用計數管理時,方法copyWithZone:及copy的呼叫者就是物件的所有者。因此該返回值不適用於autorelease。

對常數物件的類而言,定義方法copyWithZone:不一定要生成新物件。採用手動引用計數管理方式的情況下,透過self適用retain來返回結果。適用ARC和垃圾回收時,只返回self。

此外,還有另一個函式NSCopyObject(),它會將例項物件當作二進位制序列完整地複製下來並生成另一個物件。但該函式容易出錯,比較危險。所以不推薦使用。特別在使用ARC的情況下絕對要禁止使用該函式。

如果資料型別為id,則不可以使用->運運算元。

實現可變複製

- (id)mutableCopy該方法是使用NSObject簡單生成的,實際上真正生成例項的方法是mutableCopyWithZone:。

- (

id)mutableCopyWithZone:(NSZone *)NSZone如果自己需要定義一個包含了常數物件和可變物件的類,那麼使用mutableCopyWithZone:方法會更加方便。

歸檔

物件的歸檔

定義:將物件打包成二進位制檔案就稱為歸檔(archive)。

作用:將程式中使用的多個物件及其屬性值,以及它們的相互關係儲存到檔案中,或者傳送給另外的程式。

例如,將Xcode開發環境中構造的GUI物件的關係儲存到名為nib的檔案中,該檔案就包括了各物件的屬性值及物件之間的關係。這樣就可以在開發環境中再次打來該檔案進行編輯,或者在應用時使其作為實際的物件執行。

此外,在製作包含複製和貼上,拖拽和釋放等功能的應用程式時,為了儲存操作物件的資訊,有時也會利用歸檔。

並不是說歸檔適用於所有的資料儲存,很多情況下,使用XML或屬性表等高通用性的格式來儲存資料,從程式的效率及穩定性上來說都會更好一些。

Foundation框架的歸檔功能

將物件儲存轉換為二進位制序列的過程稱為歸檔,打包或編碼,逆變換則稱為解檔(unarchive),解碼或物件還原。

一個物件指向其他物件,這種關係稱為物件圖。沿著物件的指向關係往下走,有時就會再次回到原來的物件,這樣的情況稱為閉環。Foundation框架可以將物件圖歸檔為書庫,其中作為出發點的物件就稱為根物件。

可以使用NSKeyedArchiver和NSKeyedUnarchiver完成物件的歸檔和解檔操作,而他們都是抽象類NSCoder的子類。

所有可以歸檔的物件都必須要適用於協議NSCoding。NSString,NSDictionary等Foundation框架的主要類都適用協議NSCoding。

協議NSCoding按照如下方式宣告:

@protocol

NSCoding

- (

void)encodWithCoder:(NSCoder

*)aCoder;//歸檔

- (

void)initWithCoder:(NSCoder

*)aDecoder;//解檔

@end

歸檔方法的定義

- (void

)encodeWithCoder:(NSCoder *)coder {

[

super

encodeWithCoder:coder];

//超類需要適用協議NSCoding

[coder encodeObject:

物件forKey:關鍵詞字串

];

//或者使用encodeConditionalObject: forKey

...

[coder encodeDouble:

實數變數forKey:關鍵詞字串

];

//有適合多種型別的方法

...

}

引數coder是NSCoder(具體為NSKeyedArchiver)的例項,該例項稱為歸檔器。如果超類適用於協議NSCoding,那麼super將呼叫encodeWithCoder對超類的例項變數進行歸檔。如果不適用,則不能呼叫。

接下來,類自身會對包含的例項變數進行歸檔。

解檔方法的定義

引數coder是NSCoder(具體為NSKeyedArchiver)的例項,該例項稱為解檔器。

- (void

)initWithCoder:(NSCoder *)coder {

self= [super

initWithCoder:coder];

//超類不適用於協議NSCoding時,建議使用[ super init ];

變數= [coder decodeObjectForKey:鍵值

] retain];

...

變數= [coder decodeDoubleForKey:鍵值

];

...

returnself

;

}

只要在編碼和解碼中指定同樣的鍵值,解碼就可以按照任意順序,即使有不能還原的變數也沒有關係。

屬性表

屬性表概況

屬性表(property list)是Cocoa環境中用來表示或儲存各種資訊的標準資料形式。它可以將陣列或詞典等物件的結構表現為字串或二進位制形式來儲存在檔案中,或者從中讀取物件的資訊。

歸檔其主要目的是在程式內部儲存和還原物件。因此並不適合用來儲存與具體的類關聯較弱的資訊。在儲存及共享和程式的內部實現關係較弱的抽象資料時,建議使用屬性表。

屬性表有ASCII碼,XML,二進位制三種格式。ASCII是表示成文字,而二進位制是表示成二進位制檔案。

在NSDictionary的介面中儲存整個屬性表時,該字典物件就稱為根詞典。字典的鍵值必須為字串。

ASCII碼格式屬性表

只能從檔案中讀入,不能寫出。它的存在主要是為了相容性。實際程式中一般不使用。

但是作為一種字串格式,這種屬性表可以很容易地取出,看起來也一目瞭然,同時也可以用文字編輯器來編輯,因此在Debug或生成小的測試程式時也很有效。

相關文章