原型模式(Prototype Pattern)是用于創建重復的對象,同時又能保證性能。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。
這種模式是實現了一個原型接口(協議),該接口(協議)用于創建當前對象的克隆。當直接創建對象的代價比較大時,則采用這種模式。
介紹
意圖:用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。
主要解決:在運行期建立和刪除原型。
何時使用: 1、當一個系統應該獨立于它的產品創建,構成和表示時。 2、當要實例化的類是在運行時刻指定時,例如,通過動態裝載。 3、為了避免創建一個與產品類層次平行的工廠類層次時。 4、當一個類的實例只能有幾個不同狀態組合中的一種時。建立相應數目的原型并克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。
如何解決:利用已有的一個原型對象,快速地生成和原型對象一樣的實例。
優點: 1、性能提高。 2、逃避構造函數的約束。
缺點: 1、配備克隆方法需要對類的功能進行通盤考慮,這對于全新的類不是很難,但對于已有的類不一定很容易,特別當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。 2、必須實現克隆接口。
應用場景:1、當我們編寫組件需要創建新的實例對象,但是又不想依賴于初始化操作(不依賴于構造器、構造方法)。2、初始化過程需要消耗非常大的資源(數據資源、系統資源等等...)。3、構造方法需要很多初始化參數時。
原型模式:
建模:
Step 1:
圖個方便,此處我們使用系統的 <NSCopying>
協議(也可以自己實現)
@protocol NSCopying
- (id)copyWithZone:(nullable NSZone *)zone;
@end
Step 2:
創建遵循 <NSCopying>
協議的user類 UserObject
,并實現協議方法
// .h
#import <Foundation/Foundation.h>
@interface UserObject : NSObject<NSCopying>
/** 姓名 */
@property(nonatomic, copy) NSString *name;
/** 性別 */
@property(nonatomic, copy) NSString *sex;
// 構造函數
- (instancetype)initWithName:(NSString *)name
sex:(NSString *)sex;
@end
// .m
#import "UserObject.h"
@implementation UserObject
- (instancetype)initWithName:(NSString *)name
sex:(NSString *)sex
{
self = [super init];
if (self) {
_name = name;
_sex = sex;
}
return self;
}
// copy修飾符的實現方法是 copyWithZone:
/**
以前開發程序時,會據此把內存分成不同的“區”(zone),而對象會創建在某個區里面。
現在不用了,每個程序只有一個區:“默認區”(default zone).
*/
- (id)copyWithZone:(NSZone *)zone {
UserObject *copyUser = [[[self class] allocWithZone:zone] initWithName:_name
sex:_sex];
return copyUser;
}
@end
Step 3:
在 ViewController 中測試下:
UserObject *user = [[UserObject alloc] initWithName:@"小明" sex:@"男"];
UserObject *copyUser = [user copy];
user.name = @"小紅";
NSLog(@"原型對象<%p>的name=%@",user,user.name);
NSLog(@"克隆對象<%p>的name=%@",copyUser,copyUser.name);
控制臺效果:
查看對象內存地址,通過copy生成了一個新的對象,所以修改
user.name
, 克隆的對象不會受到影響。
Q:這里就可以說 copy/mutableCopy 方法會進行深拷貝么?
A:錯!!!
(因為在 copyWithZone:
方法中實現了 allocWithZone:
,會為拷貝的對象分配一個新的內存地址,從而生成了一個全新的對象返回出去)
如果我們將 copyWithZone:
方法改為如下實現:
// copy修飾符的實現方法是 copyWithZone:
- (id)copyWithZone:(NSZone *)zone {
UserObject *copyUser = [self initWithName:_name
sex:_sex
friends:_friendsArr];
return copyUser;
}
(對于我們創建的類是否是深拷貝,還要看具體的代碼實現。)
深拷貝 / 淺拷貝 這里就不再闡述了,大家可以通過下面的文章結合學習~
《Effective Objective-C 2.0》 第二十二條:理解 NSCopying 協議
iOS 集合的深復制與淺復制
demo放在GitHub上了,想查看的小伙伴可以->戳這里。
千里之行,始于足下。