美文网首页
原型模式(Prototype)

原型模式(Prototype)

作者: 傻傻小萝卜 | 来源:发表于2017-04-13 10:47 被阅读59次

基本概念

原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

从上图可以看到,Prototype类中包括一个clone方法,Client调用其拷贝方法clone即可得到实例,不需要手工去创建实例。ConcretePrototype1和ConcretePrototype2为Prototype的子类,实现自身的clone方法,如果Client调用ConcretePrototype1的clone方法,将返回ConcretePrototype1的实例。简单来理解就是根据这个原型创建新的对象,而且不需要知道任何创建的细节。打个比方,以前生物课上面,有一个知识点叫细胞分裂,细胞在一定条件下,由一个分裂成2个,再由2个分裂成4个……,分裂出来的细胞基于原始的细胞(原型),这个原始的细胞决定了分裂出来的细胞的组成结构。这种分裂过程,可以理解为原型模式。

浅复制和深复制

浅复制:只复制了指针值,并没有复制指针指向的资源(即没有创建指针指向资源的副本),复制后原有指针和新指针共享同一块内存。

深复制:不仅复制了指针值,还复制了指针指向的资源。

下面的示意图左边为浅复制,右边为深复制。

Cocoa Touch框架为NSObject的派生类提供了实现深复制的协议,即NSCopying协议,提供深复制的NSObject子类,需要实现NSCopying协议的方法(id)copyWithZone:(NSZone *)zone。NSObject有一个实例方法(id)copy,这个方法默认调用了[self copyWithZone:nil],对于引用了NSCopying协议的子类,必须实现(id)copyWithZone:(NSZone *)zone方法,否则将引发异常,异常信息如下:

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

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

代码实现

定义了一个Persion类,并实现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;

}

// 实现NSCopying中的方法

- (id)copyWithZone:(NSZone *)zone

{

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

}

@end

实现复制的代码

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

Persion *p1 = [p0 copy];// 这个操作,创建了一个新的指针,指针指向了一个新的地址,地址的内容同p0的内容一样,但是他们的内存地址是不一样的,深复制是将原内存的内容复制到一个新的内存当中

Persion *p2 = p0;// 这个操作,创建了一个新的指针,指针还是指向p0所指向的内容,指向的内存地址没有发生变化

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

NSLog(@"深拷贝(指针指向的内存地址发生变化):%p,name %@",p1,p1.name);

NSLog(@"浅拷贝(指针指向的内存地址没有发生变化):%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] 深拷贝(指针指向的内存地址发生变化):0x60000000d5b0,name 小萝卜

2017-04-13 09:47:18.927 原型模式[1043:27289] 浅拷贝(指针指向的内存地址没有发生变化):0x60000000d580,name 小萝卜

当对Persion中的name的值进行改版,观察他们name值得变化更能说明他们的的内存地址是否是同一个,如果不是同一个的话,那么他们之间是不会相互影响的,如果是同一个的话,一个进行改变,那另一个也会随之发生变化,代码如下

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

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

NSLog(@"原来p0:%p,name %@",p0,p0.name);

NSLog(@"深拷贝(指针指向的内存地址发生变化)p1:%p,name %@",p1,p1.name);

p2.name = @"快乐的小萝卜";

NSLog(@"原来p0:%p,name %@",p0,p0.name);

NSLog(@"浅拷贝(指针指向的内存地址没有发生变化)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] 原来p0:0x60800000cbd0,name 小萝卜

2017-04-13 09:58:57.881 原型模式[1215:37135] 深拷贝(指针指向的内存地址发生变化)p1:0x60800000cbe0,name 傻傻小萝卜

2017-04-13 09:58:57.881 原型模式[1215:37135] 原来p0:0x60800000cbd0,name 快乐的小萝卜

2017-04-13 09:58:57.881 原型模式[1215:37135] 浅拷贝(指针指向的内存地址没有发生变化)p2:0x60800000cbd0,name 快乐的小萝卜

通过代码我们可知:

(1)实现- (id)copyWithZone:(NSZone *)zone这个方法实现了深复制,p1 和p0 他们的内存地址不同,当修改p1中name的值时,p0的值不发生变化,更加证实了他们的内存地址通,这个可以判断为深复制

(2)而p0和p2 他们指向的内存地址是相同的,当修改p2的name的值时,p0所指向的内存地址中的值也发生了相应的变化,说明只是指向内存地址的简单复制,所以为浅复制

assign ,copy 和retain

通过一段代码,在Persion类中定义三个name属性,分别为assign,copy,和retain三种

@interface Persion : NSObject

@property (nonatomic, assign)NSString *nameAssign;

@property (nonatomic, copy)NSString *nameCopy;

@property (nonatomic, strong)NSString *nameRetain;

@end

实现代码(特别注意:在ARC下是不能够使用retainCount这个方法的,只能使用CFGetRetainCount((__bridge CFTypeRef)object) 来实现)

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);

输出结果

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"];这个代码实际上是做了两个操作,(1)在栈上为name分配了一段内存,存放的是name这个指针指向的地址0x61800006d4c0(2)在堆上分配一段内存,地址为0x61800006d4c0,内容为abc

assign ,copy 和retain

assign:默认值,当使用了assign后,nameAssign和name指向同一个地址0x61800006d4c0,且retainCount的大小没有发生变化,那么nameAssign和name共同管理0x61800006d4c0地址的内容

copy:应用copy后,会在堆上重新分配一段内存0xa000000006362613用来存放nameCopy,他们的retainCount均为1,各自管理自己指向内存的内容

retain:应用retain后,retainCount加一,共同管理内存地址0x61800006d4c0的内容

想必这样介绍完,大家对于这三个属性应该是了解的比较清楚了。这里再顺便说一下atomic和nonatomic,这两个属性用来决定编译器生成的getter和setter是否为原子操作。

atomic:默认值,提供多线程安全。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数在操作前会加锁。

nonatomic:禁用多线程的变量保护,提高性能。

atomic是OC中使用的一种线程保护技术,用来防止在写操作未完成的时候被另外一个线程读取,造成数据错误。但是这种机制是耗费系统资源的,所以如果没有使用多线程的通讯编程,那么nonatomic是一个非常好的选择。

当代码中的name,变为不可变类型NSString,输出的结果:

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

内存地址不发生变化,都是指向同一个

像NSString、NSDictionary这些类,本身已经实现了copyWithZone:(NSZone *)zone方法,直接使用如[NSString copy]调用即可。在复制后得到的副本,又可以分为可变副本(mutable copy)和不可变副本(immutable copy)。通常在NSCopying协议规定的方法copyWithZone中返回不可变副本,在NSMutableCopying协议规定的方法mutableCopyWithZone中返回可变副本,然后调用copy和mutableCopy方法来得到相应的不可变和可变副本。

参考链接:IOS设计模式浅析之原型模式(Prototype)

相关文章

网友评论

      本文标题:原型模式(Prototype)

      本文链接:https://www.haomeiwen.com/subject/czbmattx.html