原型模式(Prototype)

基本概念

原型模式:用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過(guò)拷貝這些原型創(chuàng)建新的對(duì)象


從上圖可以看到赂蠢,Prototype類中包括一個(gè)clone方法,Client調(diào)用其拷貝方法clone即可得到實(shí)例靴患,不需要手工去創(chuàng)建實(shí)例作谭。ConcretePrototype1和ConcretePrototype2為Prototype的子類洲赵,實(shí)現(xiàn)自身的clone方法厘线,如果Client調(diào)用ConcretePrototype1的clone方法识腿,將返回ConcretePrototype1的實(shí)例。簡(jiǎn)單來(lái)理解就是根據(jù)這個(gè)原型創(chuàng)建新的對(duì)象造壮,而且不需要知道任何創(chuàng)建的細(xì)節(jié)渡讼。打個(gè)比方,以前生物課上面费薄,有一個(gè)知識(shí)點(diǎn)叫細(xì)胞分裂硝全,細(xì)胞在一定條件下栖雾,由一個(gè)分裂成2個(gè)楞抡,再由2個(gè)分裂成4個(gè)……,分裂出來(lái)的細(xì)胞基于原始的細(xì)胞(原型),這個(gè)原始的細(xì)胞決定了分裂出來(lái)的細(xì)胞的組成結(jié)構(gòu)析藕。這種分裂過(guò)程召廷,可以理解為原型模式。

淺復(fù)制和深復(fù)制

淺復(fù)制:只復(fù)制了指針值账胧,并沒(méi)有復(fù)制指針指向的資源(即沒(méi)有創(chuàng)建指針指向資源的副本)竞慢,復(fù)制后原有指針和新指針共享同一塊內(nèi)存。

深復(fù)制:不僅復(fù)制了指針值治泥,還復(fù)制了指針指向的資源筹煮。

下面的示意圖左邊為淺復(fù)制,右邊為深復(fù)制居夹。



Cocoa Touch框架為NSObject的派生類提供了實(shí)現(xiàn)深復(fù)制的協(xié)議败潦,即NSCopying協(xié)議本冲,提供深復(fù)制的NSObject子類,需要實(shí)現(xiàn)NSCopying協(xié)議的方法(id)copyWithZone:(NSZone *)zone劫扒。NSObject有一個(gè)實(shí)例方法(id)copy檬洞,這個(gè)方法默認(rèn)調(diào)用了[self copyWithZone:nil],對(duì)于引用了NSCopying協(xié)議的子類沟饥,必須實(shí)現(xiàn)(id)copyWithZone:(NSZone *)zone方法添怔,否則將引發(fā)異常,異常信息如下:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Prototype copyWithZone:]: unrecognized selector sent to instance 0x100114d50'


http://www.cnblogs.com/eagle927183/p/3462439.html

代碼實(shí)現(xiàn)

定義了一個(gè)Persion類贤旷,并實(shí)現(xiàn)NSCopying的方法- (id)copyWithZone:(NSZone *)zone

@interface Persion : NSObject

@property (nonatomic, strong)NSString *name;

@end

#import "Persion.h"

@interface Persion()<NSCopying>

@end

@implementation Persion

- (instancetype)init

{

self = [super init];

if (self) {

self.name = @"小蘿卜";

}

return self;

}

// 實(shí)現(xiàn)NSCopying中的方法

- (id)copyWithZone:(NSZone *)zone

{

return [[[self class] allocWithZone:zone] init];

}

@end

實(shí)現(xiàn)復(fù)制的代碼

Persion *p0 = [[Persion alloc] init];

Persion *p1 = [p0 copy];// 這個(gè)操作广料,創(chuàng)建了一個(gè)新的指針,指針指向了一個(gè)新的地址幼驶,地址的內(nèi)容同p0的內(nèi)容一樣性昭,但是他們的內(nèi)存地址是不一樣的,深復(fù)制是將原內(nèi)存的內(nèi)容復(fù)制到一個(gè)新的內(nèi)存當(dāng)中

Persion *p2 = p0;// 這個(gè)操作县遣,創(chuàng)建了一個(gè)新的指針糜颠,指針還是指向p0所指向的內(nèi)容,指向的內(nèi)存地址沒(méi)有發(fā)生變化

NSLog(@"未修改前:%p,name %@",p0,p0.name);

NSLog(@"深拷貝(指針指向的內(nèi)存地址發(fā)生變化):%p,name %@",p1,p1.name);

NSLog(@"淺拷貝(指針指向的內(nèi)存地址沒(méi)有發(fā)生變化):%p,name %@",p2,p2.name);

2017-04-13 09:47:18.926 原型模式[1043:27289] 未修改前:0x60000000d580,name 小蘿卜

2017-04-13 09:47:18.926 原型模式[1043:27289] 深拷貝(指針指向的內(nèi)存地址發(fā)生變化):0x60000000d5b0,name 小蘿卜

2017-04-13 09:47:18.927 原型模式[1043:27289] 淺拷貝(指針指向的內(nèi)存地址沒(méi)有發(fā)生變化):0x60000000d580,name 小蘿卜

當(dāng)對(duì)Persion中的name的值進(jìn)行改版萧求,觀察他們name值得變化更能說(shuō)明他們的的內(nèi)存地址是否是同一個(gè)其兴,如果不是同一個(gè)的話,那么他們之間是不會(huì)相互影響的夸政,如果是同一個(gè)的話元旬,一個(gè)進(jìn)行改變,那另一個(gè)也會(huì)隨之發(fā)生變化守问,代碼如下

NSLog(@"未修改前p0:%p,name %@",p0,p0.name);

p1.name = @"傻傻小蘿卜";

NSLog(@"原來(lái)p0:%p,name %@",p0,p0.name);

NSLog(@"深拷貝(指針指向的內(nèi)存地址發(fā)生變化)p1:%p,name %@",p1,p1.name);

p2.name = @"快樂(lè)的小蘿卜";

NSLog(@"原來(lái)p0:%p,name %@",p0,p0.name);

NSLog(@"淺拷貝(指針指向的內(nèi)存地址沒(méi)有發(fā)生變化)p2:%p,name %@",p2,p2.name);

2017-04-13 09:58:57.880 原型模式[1215:37135] 未修改前p0:0x60800000cbd0,name 小蘿卜

2017-04-13 09:58:57.880 原型模式[1215:37135] 原來(lái)p0:0x60800000cbd0,name 小蘿卜

2017-04-13 09:58:57.881 原型模式[1215:37135] 深拷貝(指針指向的內(nèi)存地址發(fā)生變化)p1:0x60800000cbe0,name 傻傻小蘿卜

2017-04-13 09:58:57.881 原型模式[1215:37135] 原來(lái)p0:0x60800000cbd0,name 快樂(lè)的小蘿卜

2017-04-13 09:58:57.881 原型模式[1215:37135] 淺拷貝(指針指向的內(nèi)存地址沒(méi)有發(fā)生變化)p2:0x60800000cbd0,name 快樂(lè)的小蘿卜

通過(guò)代碼我們可知:

(1)實(shí)現(xiàn)- (id)copyWithZone:(NSZone *)zone這個(gè)方法實(shí)現(xiàn)了深復(fù)制匀归,p1 和p0 他們的內(nèi)存地址不同,當(dāng)修改p1中name的值時(shí)耗帕,p0的值不發(fā)生變化穆端,更加證實(shí)了他們的內(nèi)存地址通,這個(gè)可以判斷為深復(fù)制

(2)而p0和p2 他們指向的內(nèi)存地址是相同的仿便,當(dāng)修改p2的name的值時(shí)体啰,p0所指向的內(nèi)存地址中的值也發(fā)生了相應(yīng)的變化,說(shuō)明只是指向內(nèi)存地址的簡(jiǎn)單復(fù)制嗽仪,所以為淺復(fù)制

assign 荒勇,copy 和retain

通過(guò)一段代碼,在Persion類中定義三個(gè)name屬性闻坚,分別為assign沽翔,copy,和retain三種

@interface Persion : NSObject

@property (nonatomic, assign)NSString *nameAssign;

@property (nonatomic, copy)NSString *nameCopy;

@property (nonatomic, strong)NSString *nameRetain;

@end

實(shí)現(xiàn)代碼(特別注意:在ARC下是不能夠使用retainCount這個(gè)方法的窿凤,只能使用CFGetRetainCount((__bridge CFTypeRef)object) 來(lái)實(shí)現(xiàn))

Persion *p = [[Persion alloc] init];

NSMutableString *name = [[NSMutableString alloc] initWithString:@"abc"];

NSLog(@"name retainCount:%ld name:%p %@",CFGetRetainCount((__bridge CFTypeRef)name),name,name);

p.nameAssign = name;

NSLog(@"After assign name retainCount:%ld name:%p %@ nameAssign %p",CFGetRetainCount((__bridge CFTypeRef)name),name,name,p.nameAssign);

p.nameCopy = name;

NSLog(@"After copy name retainCount:%ld name:%p %@ nameCopy %p",CFGetRetainCount((__bridge CFTypeRef)name),name,name,p.nameCopy);

p.nameRetain = name;

NSLog(@"After retain name retainCount:%ld name:%p %@ nameRetain %p",CFGetRetainCount((__bridge CFTypeRef)name),name,name,p.nameRetain);

輸出結(jié)果

2017-04-13 10:22:26.157 原型模式[1564:54820] name retainCount:1 name:0x61800006d4c0 abc

2017-04-13 10:22:26.158 原型模式[1564:54820] After assign name retainCount:1 name:0x61800006d4c0 abc nameAssign 0x61800006d4c0

2017-04-13 10:22:26.158 原型模式[1564:54820] After copy name retainCount:1 name:0x61800006d4c0 abc nameCopy 0xa000000006362613

2017-04-13 10:22:26.159 原型模式[1564:54820] After retain name retainCount:2 name:0x61800006d4c0 abc nameRetain 0x61800006d4c0

首先仅偎,NSMutableString *name = [[NSMutableString alloc] initWithString:@"abc"];這個(gè)代碼實(shí)際上是做了兩個(gè)操作西潘,(1)在棧上為name分配了一段內(nèi)存,存放的是name這個(gè)指針指向的地址0x61800006d4c0(2)在堆上分配一段內(nèi)存哨颂,地址為0x61800006d4c0喷市,內(nèi)容為abc

assign ,copy 和retain

assign:默認(rèn)值,當(dāng)使用了assign后威恼,nameAssign和name指向同一個(gè)地址0x61800006d4c0品姓,且retainCount的大小沒(méi)有發(fā)生變化,那么nameAssign和name共同管理0x61800006d4c0地址的內(nèi)容

copy:應(yīng)用copy后箫措,會(huì)在堆上重新分配一段內(nèi)存0xa000000006362613用來(lái)存放nameCopy腹备,他們的retainCount均為1,各自管理自己指向內(nèi)存的內(nèi)容

retain:應(yīng)用retain后斤蔓,retainCount加一植酥,共同管理內(nèi)存地址0x61800006d4c0的內(nèi)容

想必這樣介紹完,大家對(duì)于這三個(gè)屬性應(yīng)該是了解的比較清楚了弦牡。這里再順便說(shuō)一下atomic和nonatomic友驮,這兩個(gè)屬性用來(lái)決定編譯器生成的getter和setter是否為原子操作。

atomic:默認(rèn)值驾锰,提供多線程安全卸留。在多線程環(huán)境下,原子操作是必要的椭豫,否則有可能引起錯(cuò)誤的結(jié)果耻瑟。加了atomic,setter函數(shù)在操作前會(huì)加鎖赏酥。

nonatomic:禁用多線程的變量保護(hù)喳整,提高性能。

atomic是OC中使用的一種線程保護(hù)技術(shù)裸扶,用來(lái)防止在寫操作未完成的時(shí)候被另外一個(gè)線程讀取框都,造成數(shù)據(jù)錯(cuò)誤。但是這種機(jī)制是耗費(fèi)系統(tǒng)資源的姓言,所以如果沒(méi)有使用多線程的通訊編程瞬项,那么nonatomic是一個(gè)非常好的選擇。

當(dāng)代碼中的name何荚,變?yōu)椴豢勺冾愋蚇SString,輸出的結(jié)果:

2017-04-13 10:44:00.838 原型模式[1831:70444] name retainCount:1152921504606846975 name:0x10d807080 abc

2017-04-13 10:44:00.838 原型模式[1831:70444] After assign name retainCount:1152921504606846975 name:0x10d807080 abc nameAssign 0x10d807080

2017-04-13 10:44:00.839 原型模式[1831:70444] After copy name retainCount:1152921504606846975 name:0x10d807080 abc nameCopy 0x10d807080

2017-04-13 10:44:00.839 原型模式[1831:70444] After retain name retainCount:1152921504606846975 name:0x10d807080 abc nameRetain 0x10d807080

內(nèi)存地址不發(fā)生變化猪杭,都是指向同一個(gè)

像NSString餐塘、NSDictionary這些類,本身已經(jīng)實(shí)現(xiàn)了copyWithZone:(NSZone *)zone方法皂吮,直接使用如[NSString copy]調(diào)用即可戒傻。在復(fù)制后得到的副本税手,又可以分為可變副本(mutable copy)和不可變副本(immutable copy)。通常在NSCopying協(xié)議規(guī)定的方法copyWithZone中返回不可變副本需纳,在NSMutableCopying協(xié)議規(guī)定的方法mutableCopyWithZone中返回可變副本芦倒,然后調(diào)用copy和mutableCopy方法來(lái)得到相應(yīng)的不可變和可變副本。

參考鏈接:IOS設(shè)計(jì)模式淺析之原型模式(Prototype)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末不翩,一起剝皮案震驚了整個(gè)濱河市兵扬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌口蝠,老刑警劉巖器钟,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異妙蔗,居然都是意外死亡傲霸,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門眉反,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)昙啄,“玉大人,你說(shuō)我怎么就攤上這事寸五「疲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵播歼,是天一觀的道長(zhǎng)伶跷。 經(jīng)常有香客問(wèn)我,道長(zhǎng)秘狞,這世上最難降的妖魔是什么叭莫? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮烁试,結(jié)果婚禮上雇初,老公的妹妹穿的比我還像新娘。我一直安慰自己减响,他們只是感情好靖诗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著支示,像睡著了一般刊橘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上颂鸿,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天促绵,我揣著相機(jī)與錄音,去河邊找鬼。 笑死败晴,一個(gè)胖子當(dāng)著我的面吹牛浓冒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尖坤,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼稳懒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了慢味?” 一聲冷哼從身側(cè)響起场梆,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贮缕,沒(méi)想到半個(gè)月后辙谜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡感昼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年装哆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片定嗓。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蜕琴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宵溅,到底是詐尸還是另有隱情凌简,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布恃逻,位于F島的核電站雏搂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏寇损。R本人自食惡果不足惜凸郑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望矛市。 院中可真熱鬧芙沥,春花似錦、人聲如沸浊吏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)找田。三九已至歌憨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間午阵,已是汗流浹背躺孝。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工享扔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留底桂,地道東北人植袍。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像籽懦,于是被迫代替她去往敵國(guó)和親于个。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

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