1 前言
在許多物件導向的應用程式中,有些對象的建立代價過於大或者過於複雜。要是可以重建相同的對象並作輕微的改動,事情會容易許多。我們可以通過輕微的改動重用已有的對象,以適應程式中的特定情況。今天我們就來學習一下該模式。
2 詳述
2.1 定義
應用於“複製”操作的模式成為原型(Prototype)模式。複製(cloning)指用同一模具生產一系列的產品。模具所基於的物品稱為原型。儘管產品是用同一模具複製的,但是某些屬性,如顏色與尺寸,可以稍有不同,但是他們還是屬於同一類。
2.2 何時是用原型模式
(1)需要建立的對象應獨立於其類型與建立方式。
(2)要執行個體化的類是在運行時決定的。
(3)不想要與產品層次相對應的工廠層次。
(4)不同類的執行個體間的差異僅是狀態的若干組合。因此複製相應數量的原型比手工執行個體化更加方便。
(5)類不容易建立,比如每個組件可以把其他組件作為子節點的組合對象。複製已有的組合對象並對副本進行修改會更加容易。
此模式的最低限度是產生對象的真實副本,以用作同一環境下其他相關事物的基礎(原型)。
2.3 淺複製與深複製
深複製就是開闢新記憶體實現真正的記憶體複製, 淺複製, 只複製指標, 堆記憶體不變. 在我們設計系統時, 有時一些對象需要根據使用者操作完成拷貝備份等操作, 這時候, 如果再去按照原來的方法初始化一遍對象就會帶來一些不便和問題:
(1)該對象的某些屬性是在使用者操作過程中產生的, 不能夠僅憑一個initXXX方法賦值;
(2)常規賦值太過麻煩, 而且破壞封裝.
這時候原型模式的優勢便體現出來了。
3.Demo
首先建立一個Player類, 擁有2個屬性highestLevel和currentLevel, 同時提供2個public方法修改這2個屬性. 代碼如下:
複製代碼 代碼如下:
@interface Player : NSObject <NSCopying>
/**
* update player's current level during game
*
* @param level
*/
- (void)updateCurrentLevel:(NSInteger)level;
/**
* update player's highest level during game
*
* @param level
*/
- (void)updateHighestLevel:(NSInteger)level;
@end
最為關鍵的是Player需要實現NSCopying協議:
複製代碼 代碼如下:
#pragma mark - Override
- (instancetype)copyWithZone:(NSZone *)zone
{
Player *copyPlayer = [[[self class] allocWithZone:zone] init];
copyPlayer.highestLevel = self.highestLevel;
copyPlayer.currentLevel = self.currentLevel;
return copyPlayer;
}
這裡大家看到NSZone類型, 這是個什麼類型呢? 其實它是一個結構體, 是為了防止記憶體片段化而引入的一個結構. NSZone會根據你想要開闢的記憶體大小來分配記憶體, 提高記憶體管理. 然而官方的Programming with ARC Release Note也指出, 目前的runtime系統忽略了地區的概念,因為本身的記憶體管理已經非常有效率,使用Zone反而會降低記憶體使用量,訪問效率, 增加原始碼複雜度等.所以一般不使用NSZone, 而在這個例子中, 雖說使用了allocWithZone的方法, 但是我們進去看原始碼則會發現: Apple其實還是用一般的初始化方法代替了原來的Zone開闢:
複製代碼 代碼如下:
#pragma mark - Override
- (instancetype)copyWithZone:(NSZone *)zone
+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object
initializers instead");
原型設計模式基本就是這些, 當然我們的Player類可以變成一個介面, 讓子類去實現, 更好的體現面向介面編程.
結果:
2015-09-18 21:30:32.072 DP_Prototype[1173:280693] <Player: 0x14d513f60>2015-09-18 21:30:32.073 DP_Prototype[1173:280693] <Player: 0x14d5337e0>
在其他檔案調用copy方法, 即可看到系統為我們新開闢的一塊記憶體, 引用計數為1.
4.Cocoa Touch架構中的對象複製
CocoaTouch架構為NSObject的衍生類別提供了實現深複製的協議。NSObject的子類需要實現NSCopying協議及其方法--(id)copyWithZone:(NSZone *)zone。NSObject有一個執行個體方法叫做(id)copy。預設的copy方法調用[selfcopyWithZone:nil]。對於採納了NSCopying協議的子類,需要實現這個方法,否則將引發異常。IOS中,這個方法保持新的副本對象,然後將其返回。此方法的調用者需要負責釋放返回的對象。
深複製的技巧在於:保證確實複製了記憶體中的資源,而不只是指指標。