使用原型模式实例创建对象的种类,并通过复制这个原型创建新的对象
客户端知道抽象Prototype类,在运行时,抽象Prototype子类的任何对象都可以案客户端的意愿被复制,因此,无需手工创建就可以制造同一类的多个实例。
需要创建的对象应独立于其类型与创建方式
要实例化的类是在运行时决定的
不想要与产品层次相对应的工厂层次
不同类的实例间的差异仅是状态的若干组合,因此复制相应数量的原型比手工实例化更加方便
类容易创建,比如每个组件可把其他组件作为子节点的组合对象。复制已有的组合对象并对副本进行修噶会更加容易
如:有多相关的类,其行为略有不同,而且主要差异在于内部属性,如名称,图像等
需要使用组合(树形)对象作为其他东西的基础,例如,在使用组合对象作为组件来构建另一个组合的对象
有个使用原型对象的常见误解,原型对象是典型对象,从不实际使用,这一误解专注于实现次模式的一种特定方式,从功能的角度来看,不管什么对象,只要复制自身比手工要好,都可以是原型对象
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { //只要是拷贝出来的额对象,拷贝出来的对象中的内容和以前对象中的内容一致 //“一般”情况下拷贝会生成一个新的对象 //为什么会产生一个新的对象 1.因为拷贝要求修改原来的对象不能影响到拷贝出来的对象 //修改拷贝出来的对象也不能影响到原来的对象,所以需要生成一个新的对象 //2.由于以前的对象是一个不可变的对象,而是通过mutableCopy拷贝出来的对象必须是一个可变的对象,所以必须生成一个新的对象 //会生成新的一个新的对象 /* //为什么会产生新的对象,1.因为拷贝 NSString *scrStr=@"qll"; NSMutableArray *copystr=[scrStr mutableCopy]; NSLog(@"scrStr=%@,copyStr=%@",scrStr,copystr);*/ //会生成新的一个新的对象 /* NSMutableString *srcStr=[NSMutableString stringWithFormat:@"qll"]; NSMutableString *copyStr=[srcStr mutableCopy]; [srcStr appendString:@" COOL"]; NSLog(@"srcStr=%@",srcStr); NSLog(@"copyStr=%@",copyStr);*/ //会生成新的一个新的对象 /* NSMutableString *srcStr=[NSMutableString stringWithFormat:@"all"]; NSString *copyStr=[srcStr copy]; [srcStr appendString:@" cool"]; NSLog(@"scrStr=%@,copyStr=%@",srcStr,copyStr);*/ //没有生成新的对象 //原因:因为原来的对象是不能修改的,拷贝出来的对象一晚上不能修改的 //既然两个都不能修改,所以永远不能影响到另外一个对象,那么已经符合要求 //所以:OC为了对内存进行优化,就不会生成一个新的对象 NSString *str=@"lnj"; NSString *copyStr=[str copy]; NSLog(@"%p, %p",str,copyStr); /* 如果,没有生成新的对象,我们称之为浅拷贝,本质就是指针拷贝 如果,生成了新的对象,我们称之为深拷贝,本质就是创建了一个新的对象,内容拷贝 */ } return 0; }
Cocoa Touch框架为NSObject的派生类提供了实现深复制的协议。NSObject的子类需要实现NSCopying协议机器方法----(id)copyWithZone:(NSZone *)zone。NSObject有一个实力方法叫做(id)copy。默认的copy方法调用[self copyWithZone:nil],对于采纳了NSCopying协议的子类,需要实现这个方法,否则引发异常。iOS中,这个保持新的副本对象,然后将其返回,此方法的调用要负责释放返回的对象
Resume.h文件
#import <Foundation/Foundation.h> @interface Resume : NSObject<NSCopying,NSMutableCopying> @property(copy,nonatomic)NSString *name; @property(nonatomic,copy)NSString *sex; @property(nonatomic,assign)int age; @property(copy,nonatomic)NSString *timeArea; @property(copy,nonatomic)NSString *company; @end
Resume.m文件
#import "Resume.h" @implementation Resume - (NSString *)description { return [NSString stringWithFormat:@"name=%@,age=%d,sex=%@,timeArea=%@,company=%@", _name,_age,_sex,_timeArea,_company]; } -(id)copyWithZone:(NSZone *)zone{ Resume *resume=[[[Resume class] allocWithZone:zone]init]; resume.age=_age; resume.name=_name; resume.sex=_sex; resume.timeArea=_timeArea; resume.company=_company; return resume; } -(id)mutableCopyWithZone:(NSZone *)zone{ return [self copyWithZone:zone]; } @end
客户端
#import <Foundation/Foundation.h> #import "Resume.h" int main(int argc, const char * argv[]) { @autoreleasepool { Resume *a=[Resume new]; a.name=@"大鸟"; a.age=20; a.sex=@"男"; a.company=@"百度"; a.timeArea=@"2016-2017"; Resume *b=[a copy]; b.company=@"腾讯"; b.timeArea=@"2014-2015"; NSLog(@"%@",a); NSLog(@"%@",b); } return 0; }
效果
假如有个子类继承Resume类的话,并且子类有自己特有的属性,如果其想要使用期特有的属性,需要重写
(id)copyWithZone:(NSZone *)zone方法
例如:子类
workExperience.h文件
#import "Resume.h" @interface workExperience : Resume @property(copy,nonatomic)NSString *workdate; @end
workExperience.m文件
@implementation workExperience - (NSString *)description { return [NSString stringWithFormat:@"name=%@,age=%d,sex=%@,timeArea=%@,company=%@,workDate=%@",[self name],[self age],[self sex],[self timeArea],[self company],_workdate]; } -(id)copyWithZone:(NSZone *)zone{ id obj=[[self class] allocWithZone:zone]; [obj setAge:[self age]]; [obj setName:[self name]]; [obj setAge:[self age]]; [obj setCompany:[self company]]; [obj setTimeArea:[self timeArea]]; [obj setWorkdate:_workdate]; return obj; } @end
客户端
workExperience *work=[[workExperience alloc]init]; work.name=@"大鸟"; work.age=20; work.sex=@"男"; work.company=@"百度"; work.timeArea=@"2016-2017"; work.workdate=@"8小时"; workExperience *work1=[work copy]; work1.workdate=@"12小时"; NSLog(@"%@",work); NSLog(@"%@",work1);
当需要生成真正的副本的时候,必须要实现深复制,这样才可以对对象进行后续的操作