NSCopying
NSCopying是一个与对象拷贝有关的协议。如果想让一个类的对象支持拷贝,就需要让该类实现NSCopying协议,如果不实现这个协议,直接copy的话
Person * person = [[Person alloc] init];
Person * copyPerson = person.copy;
当我们的类实现了NSCopying协议,通过类的对象调用copy方法时,copy方法就会去调用我们实现的- (id)copyWithZone:(NSZone *)zone方法,实现拷贝功能
说明:在- (id)copyWithZone:(NSZone *)zone方法中,一定要通过[self class]方法返回的对象调用allocWithZone:方法。因为指针可能实际指向的是Person的子类。这种情况下,通过调用[self class],就可以返回正确的类的类型对象
-(id)copyWithZone:(NSZone *)zone{
Person * p = [[[self class] allocWithZone:zone] init];
p.name = self.name;
p.city = self.city;
//未公开的成员
p->provice = provice;
return p;
}
调用
//创建一个实例
Person * person = [[Person alloc] init];
person.name = @"edison";
NSLog(@"Person =%@ name=%@",person,person.name);
//通过person深复制出一个新的对象prototypeCopy
Person * copyPerson = person.copy;
NSLog(@"copyPerson =%@ name=%@",copyPerson,copyPerson.name);
// 通过person直接赋值,其实就是复制了指针,属于浅复制,引用计数不变
Person * shallowCopyPerson = person;
NSLog(@"shallowCopyPerson =%@ name=%@",shallowCopyPerson,shallowCopyPerson.name);
打印结果如下
2018-09-26 15:29:30.986120+0800 设计模式-原型模式[1102:192429] Person =<Person: 0x600000221180> name=edison
2018-09-26 15:29:30.986288+0800 设计模式-原型模式[1102:192429] copyPerson =<Person: 0x60000022d280> name=edison
2018-09-26 15:29:30.986408+0800 设计模式-原型模式[1102:192429] shallowCopyPerson =<Person: 0x600000221180> name=edison
NSMutableCopying
NSCopying协议与NSMutableCopying的区别主要是在于,返回的对象是否是可变类型的。以Foundation框架的NSArray为例
NSArray对象调用copy方法时,copy方法会调用- (id)copyWithZone:(NSZone *)zone,得到对象的一份拷贝,但得到的对象还是不可变的对象。而NSArray对象调用mutableCopy方法时,mutableCopy方法会调用- (id)mutableCopyWithZone:(NSZone *)zone,得到可变的对象。
如果类想要支持mutableCopy操作,则必须实现NSMutableCopying协议,也就是说实现mutableCopyWithZone
方法
在- (id)copyWithZone:(NSZone *)zone
返回的是不可变对象,在- (id)mutableCopyWithZone:(NSZone *)zone
返回的是可变对象
- (id)mutableCopyWithZone:(nullable NSZone *)zone{
Person * p = [[[self class] allocWithZone:zone] init];
p.name = self.name;
p.city = self.city;
//arr是可变数组
p.arr = [self.arr mutableCopyWithZone:zone];
//未公开的成员
p->provice = provice;
return p;
}
原型模式的概念
使用原型模式场景
- 需要创建的对象应独立于其类型与创建方式。
- 要实例化的类是在运行时决定的。
- 不想要与产品层次相对应的工厂层次。
- 不同类的实例间的差异仅是状态的若干组合。因此复制相应数量的原型比手工实例化更加方便。
- 类不容易创建,比如每个组件可以把其他组件作为子节点的组合对象。复制已有的组合对象并对副本进行修改会更加容易。
以下两种特别常见的情形,我们会想到用原型模式:
- 有很多的相关的类,其行为略有不同,而且主要差异在于内部属性,如名称等;
- 需要使用组合(树)对象作为其他对象的基础,比如,使用组合对象作为组件来构建另一个组合对象