原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另一个可定制的对象,而且不需知道任何创建的细节。
比如说,有一个Person类,有firstName、lastName、friends这三个属性,代码如下:
#import <Foundation/Foundation.h>
@interface ZYPerson : NSObject
{
NSMutableSet *_friends;
}
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;
- (void)addFriend:(ZYPerson *)person;
- (void)removeFriend:(ZYPerson *)person;
@end
#import "ZYPerson.h"
@implementation ZYPerson
- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName
{
if (self = [super init]) {
_firstName = firstName;
_lastName = lastName;
_friends = [[NSMutableSet alloc] init];
}
return self;
}
- (void)addFriend:(ZYPerson *)person
{
[_friends addObject:person];
}
- (void)removeFriend:(ZYPerson *)person
{
[_friends removeObject:person];
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
ZYPerson *personOne = [[ZYPerson alloc] initWithFirstName:@"张" lastName:@"三"];
ZYPerson *personTwo = [[ZYPerson alloc] initWithFirstName:@"李" lastName:@"四"];
[personOne addFriend:personTwo];
}
现在有这样的一个需求,有一个人,也叫张三,也只有李四一个好友,如果不用原型模式,就会使下面的代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
ZYPerson *personOne = [[ZYPerson alloc] initWithFirstName:@"张" lastName:@"三"];
ZYPerson *personTwo = [[ZYPerson alloc] initWithFirstName:@"李" lastName:@"四"];
[personOne addFriend:personTwo];
ZYPerson *personThree = [[ZYPerson alloc] initWithFirstName:personOne.firstName lastName:personOne.lastName];
[personThree addFriend:personTwo];
}
这样,Person类只有两三个属性还好说,只是简单的写下,如果Person类有十几个属性,有上百个朋友,这代码量是很大的。
如此,原型模式就可以比较好的解决这样一个问题,在iOS开发中,原型模式依赖于NSCopying协议,需要实现-copyWithZone方法,Person类代码如下:
#import <Foundation/Foundation.h>
@interface ZYPerson : NSObject <NSCopying>
{
NSMutableSet *_friends;
}
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;
- (void)addFriend:(ZYPerson *)person;
- (void)removeFriend:(ZYPerson *)person;
@end
#import "ZYPerson.h"
@implementation ZYPerson
- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName
{
if (self = [super init]) {
_firstName = firstName;
_lastName = lastName;
_friends = [[NSMutableSet alloc] init];
}
return self;
}
- (void)addFriend:(ZYPerson *)person
{
[_friends addObject:person];
}
- (void)removeFriend:(ZYPerson *)person
{
[_friends removeObject:person];
}
- (id)copyWithZone:(NSZone *)zone
{
ZYPerson *copy = [[[self class] allocWithZone:zone] initWithFirstName:_firstName lastName:_lastName];
copy->_friends = [_friends mutableCopy]; // 虽然是深拷贝,但是只是对集合进行拷贝
//内部的指针,没有真正拷贝元素。 这里是不能拷贝元素的,比如p2改名字了, 这个两个人的好友里都会更新p2信息了,符合逻辑
// 如果需要拷贝集合内部的元素 NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
// 比如 有两个班级, 班级成员在一个数组里,这是就不能用 mutableCopy , 需要用 copyitem 。
return copy;
}
@end
viewController代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
ZYPerson *personOne = [[ZYPerson alloc] initWithFirstName:@"张" lastName:@"三"];
ZYPerson *personTwo = [[ZYPerson alloc] initWithFirstName:@"李" lastName:@"四"];
[personOne addFriend:personTwo];
ZYPerson *personThree = [personOne copy];
NSLog(@"%@ %@",personThree.firstName, personThree.lastName);
}
可以看到,在初始化条件不发生改变的情况下,copy是最好的办法,既隐藏了对象创建的细节,对性能也是有着显著提高的,最主要的一点,就是没必要去重复写垃圾代码。
浅拷贝(copy)与深拷贝(mutableCopy)
copy与mutableCopy的区别,如果学过c的朋友会知道指针这样一个概念,就是在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存
而深拷贝就是不仅增加了一个指针,并且还申请了一个新的内存,是这个新增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误。
引申出这样一个问题(面试题):怎样使用copy关键字?
一般使用retain或者strong修饰属性时,是使引用对象的指针指向同一个对象,即为同一块内存地址。只要其中有一个指针变量被修改时所有其他引用该对象的变量都会被改变。
而copy关键字修饰时,如果新的对象是不可变的,那么它是直接引用新对象的内存地址,并不重新分配内存地址,如果新对象是可变的,那么在赋值时是释放旧对象,拷贝新对象内容。重新分配了内存地址。以后该指针变量被修改时就不会影响旧对象的内容了。
copy只有实现NSCopying协议的对象类型才有效。
对于容器而言,深浅拷贝对于其元素对象始终是指针复制。
下面的方法可以对元素进行深拷贝, 将集合内部的元素也进行了深拷贝。
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"],[NSStringstringWithString:@"b"],@"c",nil];
// 更推荐使用下面的, array内部的元素需要都实现copy方法
NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];