基本概念
原型模式:用原型實(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è)