在许多iOS和MAC的应用程序中,有些对象的创建代价过大或过于复杂,此时若是可以只作轻微的改动就可以重建相同的对象,以适应程序中的特定情况,那真是极好的。比较典型的情况就是复制组合结构,比如树型结构,从零开始构建一个树型组合体非常困难。与创建各种跟父类差异较少的独立类相比,让某些对象生成自身的复制品这种做法可复用性极高并且更易于维护。
应用于“复制”操作的模式称为原型模式,复制指用同一模具生产一系列的产品,模具所基于的物品称为原型。原型决定了最终产品应该是什么样子,尽管产品是用同一模具复制的,但是某些属性可以稍有不同。
模型图
如果想要较好的使用原型模式,那么有必要对浅复制和深复制有比较深刻的理解。下面我将用图示的方式直观方便的解析它们的异同
浅复制
如果对象有个指针型成员变量指向内存中的某个资源,如果只是将指针复制给新对象(副本),那么底层的资源实际上仍然由两个实例在共享。如果所示:
在ConcretePrototype的clone操作中,把资源指针的值复制到了新的副本。尽管ConcrePrototype的实例生成了一个同类型的实例作为其副本,但两个实例的指针仍指向内存中的同一资源。这种只复制了指针值而不是实际资源,称为浅复制。
深复制
那么什么是深复制呢?深复制是指不仅复制指针值,还复制指针所指向的资源,生成内存中实际资源的真正副本。示意图如下:
Cocoa Touch中的对象复制
Cocoa Touch Framework为NSObject的派生类提供了实现深复制的协议。NSObject的子类需要实现NSCopying协议及其方法:
(id)copyWithZone:(NSZone *)zone
NSObject有一个实例方法叫做(id) copy,默认的copy方法调用[ self copyWithZone : nil ]。对于采纳了NSCopying协议的子类,需要实现这个方法,否则将引发异常。iOS中,这个方法保持新的副本对象,然后将其返回。特别地,此方法的调用者要负责释放返回的对象。
多数情况下,深复制的实现看起来并不很复杂。其思路是复制必需的成员变量与资源,传给此类的新实例,然后返回这个新实例。技巧在于保证确实复制了内存中的资源,而不是指针值。
应用场景
在以下情形,可以考虑使用原型模式:
①、需要创建的对象应独立于其类型与创建方式
②、要实例化的类是在运行时决定的
③、不想要与产品层次相对应的工厂层次
④、不同类的实例间的差异仅是状态的若干组合,因此复制相应数量的原型比手工实例化更加方便
⑤、类不容易创建,比如每个组件可把其他组件作为子节点的组合对象。复制已有的组合对象并对副本进行修改会更加容易。