设计模式系列-原型

使用设计模式更像艺术行为而非科学行动。是为了让你的代码更加优雅。


一、摘要

本文主要是讲述创建型模式中一个比较特殊的模式-原型模式,这个模式呢,有个最大的特点是克隆一个现有的对象,这个克隆的结果有2种,一种是是浅复制,另一种是深复制,这里我们也会探讨下深复制和浅复制的原理,这样可能更方便大家理解这个原型模式的使用。我们都知道,创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些对象的操作,我们通过原型模式可以快速的创建一个对象而不需要提供专门的new()操作就可以快速完成对象的创建,这无疑是一种非常有效的方式,快速的创建一个新的对象。本文将会从以下几个方面进行讲述:

       1、原型模式的使用场景和特点

       2、浅复制和深复制的原理。

       3、举例说明浅复制和深复制。

       4、原型模式的实现方案。

       5、总结原型模式。

     我们这里先给出一个原型模式的原理图:


二、原型模式的特点及使用场景

原型模式的主要思想是基于现有的对象克隆一个新的对象出来,一般是由对象的内部提供克隆的方法,通过该方法返回一个对象的副本,这种创建对象的方式,相比我们之前说的几类创建型模式还是有区别的,之前的讲述的工厂模式与抽象工厂都是通过工厂封装具体的new操作的过程,返回一个新的对象,有的时候我们通过这样的创建工厂创建对象不值得,特别是以下的几个场景的时候,可能使用原型模式更简单也效率更高。


1、如果说我们的对象类型不是刚开始就能确定,而是这个类型是在运行期确定的话,那么我们通过这个类型的对象克隆出一个新的类型更容易。这个怎么理解。例如我们有的时候在处理DataTable中的记录进行筛选后,放在一个新的DataTable 中,我们知道如果说2个dataTable的架构不同,那么必须手动的显示的赋值,否则无法使用如下方式进行导入数据:


2、有的时候我们可能在实际的项目中需要一个对象在某个状态下的副本,这个前提很重要,这点怎么理解呢,例如有的时候我们需要对比一个对象经过处理后的状态和处理前的状态是否发生过改变,可能我们就需要在执行某段处理之前,克隆这个对象此时状态的副本,然后等执行后的状态进行相应的对比,这样的应用在项目中也是经常会出现的。



3、当我们在处理一些对象比较简单,并且对象之间的区别很小,可能只是很固定的几个属性不同的时候,可能我们使用原型模式更合适,例如我们生活中的彩虹的七彩的颜色,等等,我们只需要根据现有的一个颜色对象,克隆一个新的颜色对象,然后修改具体的颜色的值就可以满足要求,然后如果通过我们之前讲述的创建型工厂,抽象工厂模式等相对来说就引入新的依赖,并且复杂度也有所提高。例如我们的生活中的颜色的克隆:


我们都可以通过红色来克隆其他的所有颜色,只是修改相应的个别属性即可,远比创建一个新的对象,然后给对象的各个属性赋值来的简单和方便,当然有的时候,如果我们并不需要基于现有的对象复制新的对象,或者我们需要的就是一个干净的空对象,那么我的首先还是工厂模式或者抽象工厂模式啦。





浅析ObjectiveC 深浅拷贝学习

在ObjectiveC 中,什么是深浅拷贝? 深浅拷贝分别指深拷贝和浅拷贝,即mutableCopy和copy方法。

copy复制一个不可变对象,而mutableCopy复制一个mutable可变对象。

什么时候用到深浅拷贝?下面举几个例子说明。 非容器类对象 如NSString,NSNumber等一类对象  

示例1:

// 非容器类对象     
	NSString *str = @"origin string";  
	NSString *strCopy = [str copy];
	NSMutableString *mstrCopy = [str mutableCopy];  
	[mstrCopy appendString:@"??"];
	NSLog(@"%p", str);
	NSLog(@"%p", strCopy);
	NSLog(@"%p", mstrCopy);

输出:

2012-04-1708:00:44.156 Prototype[3139:207]0x354c

2012-04-1708:00:44.157 Prototype[3139:207]0x354c

2012-04-1708:00:44.158 Prototype[3139:207]0x6a05230


查看内存可以发现,str和strCopy指向的是同一块内存区域,我们称之为弱引用(weak reference),只是原对象的引用计数+1。而mstrCopy是真正的复制,系统为其分配了新内存空间,保存从str复制过来的字符串值,且新对象是nsmutablestring。从最后一行代码中修改这些值而不影响str和strCopy中可证明。


示例2:

	NSMutableString *mstr = [NSMutableString stringWithString:@"origin"];  
	NSString *strCopy = [mstr copy];  
	NSLog(@"%@", [strCopy class]);
	NSMutableString *mstrCopy = [mstr copy];  
	NSMutableString *mstrMCopy = [mstr mutableCopy];  
//	[mstrCopy appendString:@"1111"];  //error  
	[mstr appendString:@"222"];  
	[mstrMCopy appendString:@"333"];
	NSLog(@"%p", mstr);
	NSLog(@"%p", strCopy);
	NSLog(@"%p", mstrCopy);
	NSLog(@"%p", mstrMCopy);

输出:

2012-04-1708:44:50.442 Prototype[3259:207]0x68172e0

2012-04-1708:44:50.443 Prototype[3259:207]0x681a4c0

2012-04-1708:44:50.443 Prototype[3259:207]0x6a0fcf0

2012-04-1708:44:50.443 Prototype[3259:207]0x6a0bc60

以上四个对象所分配的内存都是不一样的。而且对于mstrCopy,它所指向的其实是一个imutable对象,是不可改变的,所以会出错。copy产生的新对象都是nsstring类型。这点要注意,好好理解。


容器类对象深浅复制

比如NSArray,NSDictionary等。对于容器类本身,上面讨论的结论也适用的,下面探讨的是复制后容器内对象的变化。

示例3

/* copy返回不可变对象,mutablecopy返回可变对象 */   
    NSArray *array1     = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
    NSArray *arrayCopy1 = [array1 copy];
    //arrayCopy1是和array同一个NSArray对象(指向相同的对象),包括array里面的元素也是指向相同的指针  
    NSLog(@"array1 retain count: %d",[array1 retainCount]);
    NSLog(@"array1 retain count: %d",[arrayCopy1 retainCount]);
	NSLog(@"%p", array1);
	
    NSMutableArray *mArrayCopy1 = [array1 mutableCopy];  
    //mArrayCopy1是array1的可变副本,指向的对象和array1不同,但是其中的元素和array1中的元素指向的还是同一个对象。mArrayCopy1还可以修改自己的对象  
    [mArrayCopy1 addObject:@"de"];  
    [mArrayCopy1 removeObjectAtIndex:0]; 
	NSLog(@"%d", [mArrayCopy1 retainCount]);

rray1和arrayCopy1是指针复制,而mArrayCopy1是对象复制,符合前面示例1讨论的结论。mArrayCopy1可以改变其内的元素:删除或添加。但 容器内的元素内容都是浅拷贝


示例4

NSArray *mArray1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];  
    NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);  
    NSArray *mArrayCopy2 = [mArray1 copy];  
    NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);  
    // mArray1和mArrayCopy2指向同一对象,retain值+1。  
	
    NSMutableArray *mArrayMCopy1 = [mArray1 mutableCopy]; 
	[mArrayMCopy1 addObject:@"d"];
    NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);     //mArrayCopy2和mArray1指向的是不一样的对象,但是其中的元素都是一样的对象——同一个指针  
	
    NSMutableString *testString = [mArray1 objectAtIndex:0];   
	
    //testString = @"1a1";//这样会改变testString的指针,其实是将@“1a1”临时对象赋给了testString  
    [testString appendString:@" tail"];//这样以上三个数组的首元素都被改变了

由此可见, 对于容器而言,其元素对象始终是指针复制 。如果需要元素对象也是对象复制,就需要实现深拷贝。


示例5

NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithFormat:@"first"],[NSString stringWithFormat:@"b"],@"c",nil];  
	NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES]; 

	NSLog(@"%p",[array objectAtIndex:0]);
	NSLog(@"%p",[deepCopyArray objectAtIndex:0]);
	NSLog(@"%p",[array objectAtIndex:1]);
	NSLog(@"%p",[deepCopyArray objectAtIndex:1]);
	
	NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:  
								  [NSKeyedArchiver archivedDataWithRootObject: array]];
	NSLog(@"%p",[trueDeepCopyArray objectAtIndex:0]);
	NSLog(@"%p",[trueDeepCopyArray objectAtIndex:1]);

输出

2012-04-1710:19:43.130 Prototype[4112:207]0x6a1f710

2012-04-1710:19:43.131 Prototype[4112:207]0x6a1f7c0

2012-04-1710:19:43.132 Prototype[4112:207]0x6a11970

2012-04-1710:19:43.132 Prototype[4112:207]0x6a11970

2012-04-1710:19:48.762 Prototype[4112:207]0x6a1f880

2012-04-1710:19:49.349 Prototype[4112:207]0x6a1f930


trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则不是,对于deepCopyArray内的不可变元素其还是指针复制。

或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。

举个例子,[[array objectAtIndex:0]appendstring:@”sd”]后其他的容器内对象并不会受影响。[[array objectAtIndex:1]和[[deepCopyArray objectAtIndex:0]尽管是指向同一块内存,但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全意义上的深拷贝


自定义对象  

如果是我们定义的对象,那么我们自己要实现NSCopying,NSMutableCopying这样就能调用copy和mutablecopy了。举个例子:

@interface MyObj : NSObject<NSCopying ,NSMutableCopying> 
{  
	NSMutableString *name;  
	NSString *imutableStr;  
	int age;  
}  
@property (nonatomic, retain) NSMutableString *name;  
@property (nonatomic, retain) NSString *imutableStr;  
@property (nonatomic) int age;  

@end  

@implementation MyObj  
@synthesize name;  
@synthesize age;  
@synthesize imutableStr;  
- (id)init  
{  
	if (self = [super init])  
	{  
		self.name = [[NSMutableString alloc]init];  
		self.imutableStr = [[NSString alloc]init];  
		age = -1;  
	}  
	return self;  
}  
- (void)dealloc  
{  
	[name release];  
	[imutableStr release];  
	[super dealloc];  
}  
- (id)copyWithZone:(NSZone *)zone  
{  
	MyObj *copy = [[[self class] allocWithZone:zone] init];  
	copy.name = [name copy];  
	copy.imutableStr = [imutableStr copy]; 
	copy.age = age;
	
	return copy;  
}  
- (id)mutableCopyWithZone:(NSZone *)zone  
{  
	MyObj *copy = NSCopyObject(self, 0, zone);  
	copy.name = [self.name mutableCopy];  
	copy.age = age;  
	return copy;  
}  
@end












  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值