iOS常用设计模式——原型模式

原型模式的定义

原型模式(Prototype)即应用于“复制”操作的模式,此模式最初定义在《设计模式》(Addison-Wesley,1994),里面是这样定义的“使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象”。简单来理解就是根据这个原型创建新的对象,这种创建是指深复制,得到一份新的内存资源,而不是一个新的指针引用。使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象.


当一个类的组成比较复杂,例如包含多个组件或多个自定义类属性的时候,直接复制当前对象比从头开始创建对象要简单得多,则使用原型模式最为合适。又或者对象间的区别不大,只是几个属性不同的时候,也可以使用原型模式,前提是要继承同一个父类。


适用环境 


需要创建的对象应独立于其类型与创建方式。 

要实例化的类是在运行时决定的。 

不想要与产品层次相对应的工厂层次。 

不同类的实例间的差异仅是状态的若干组合。(复制相应数量的原型比手工实例化更加方便) 

类不容易创建。(复制已有的组合对象并对副本进行修改会更容易) 



主要涉及的知识:复制&copy


关于深复制与浅复制(深拷贝&浅拷贝),可以参考我博客里的一篇文章iOS中的深拷贝和浅拷贝的学习记录 这里就不过多介绍了.



我们来举一个例子,首先我们先创建一些假数据,让这些假数据看起来,很多,显得创建一个这样的item会比较难,通过这些假数据,加载到我们要操作的ViewControllview上面


创建假数据Person类


点h文件

//  PersonItem.h
//  原型模式CSDN
//
//  Created by 王颜龙 on 13-12-30.
//  Copyright (c) 2013年 longyan. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface PersonItem : NSObject<NSCopying>

@property (nonatomic, strong)UIImage        *headerImg;//头像
@property (nonatomic, assign)int            age;//年纪
@property (nonatomic, assign)CGFloat        height;//身高
@property (nonatomic, assign)CGFloat        weight;//体重
@property (nonatomic, assign)int            numID;//学号
@property (nonatomic, assign)int            classNum;//学年
@property (nonatomic, assign)int            score1;//分数1
@property (nonatomic, assign)int            score2;//分数2
@property (nonatomic, assign)int            score3;//分数3
@property (nonatomic, assign)int            score4;//分数4
@property (nonatomic, assign)int            score0;//分数0
@property (nonatomic, assign)BOOL           isChoose;//判断是否被选中

@end



加载假数据

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    
    NSMutableArray *arr = [[NSMutableArray alloc]initWithCapacity:0];
    
    //制作假数据
    for (int i = 0; i < 10; i++) {
        PersonItem *item = [[PersonItem alloc]init];
        item.headerImg = [UIImage imageNamed:[NSString stringWithFormat:@"%d",i+1]];
        item.age = 15+i;
        item.height = 165+i;
        item.weight = 120+i;
        item.numID = 100+i;
        item.classNum = 10+i;
        item.score0 = 100+i;
        item.score1 = 90+i;
        item.score2 = 80+i;
        item.score3 = 70+i;
        item.score4 = 60+i;
        item.isChoose = NO;
        [arr addObject:item];
    }
    
    EditViewController *vc = [[EditViewController alloc]initWithItem:arr];
    
    UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:vc];
    
    self.window.rootViewController = nav;
    
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

接下来,我们往项目里拖拽一个GMGridView,和一些图片数据,方便我们的展示




进入展示页面,是这样的



我们现在假设,如果我们想创建一个新的数据,但是这个数据又和之前的类似,但又有一些要修改的地方,例如我们修改头像,那么我们应该要怎么做呢? 我们可以像一开始那样重新创建一个preson类的item


PersonItem *item = [[PersonItem alloc]init];
        item.headerImg = [UIImage imageNamed:[NSString stringWithFormat:@"%d",i+1]];
        item.age = 15+i;
        item.height = 165+i;
        item.weight = 120+i;
        item.numID = 100+i;
        item.classNum = 10+i;
        item.score0 = 100+i;
        item.score1 = 90+i;
        item.score2 = 80+i;
        item.score3 = 70+i;
        item.score4 = 60+i;
        item.isChoose = NO;

但是如果要是创建100个,1000个的话,实际上这样做是不太合理的,这个时候就要用到我们的原型模式了.

再次说一下原型模式的定义:当一个类的组成比较复杂,例如包含多个组件或多个自定义类属性的时候,直接复制当前对象比从头开始创建对象要简单得多,则使用原型模式最为合适。


在这里GMGridView的初始化以及一些修改 我就不写了,很简单,重点写一下3个按钮得方法


#pragma bottomView method
//错误复制的方法
- (void)errorItem{
    if (self.chooseArr.count == 0) {
        return;
    }
    
    PersonItem *item = [self.chooseArr objectAtIndex:0];
    
    item.headerImg = [UIImage imageNamed:@"15"];
    
    [self.selectedItems addObject:item];
    
    [self.gridView reloadData];

}

//正确复制的方法
- (void)copyItem{
    if (self.chooseArr.count == 0) {
        
        return;
    }

    PersonItem *itemNew = [[self.chooseArr objectAtIndex:0] copy];
    itemNew.headerImg = [UIImage imageNamed:@"15"];
    [self.selectedItems addObject:itemNew];
    
    [self.gridView reloadData];
}

//删除按钮
- (void)delItem{
    if (self.chooseArr.count == 0) {
        
        return;
    }
    
    if (self.selectedItems.count <=1) {
        
        return;
    }
    
    [self.selectedItems removeObject:[self.chooseArr objectAtIndex:0]];
    [self.chooseArr removeAllObjects];
    [self.gridView reloadData];
}

如果你点击了错误复制的方法,你会发现




你会发现什么,你复制了新的item,但是旧的item的头像也跟着改变了,这是为什么呢?我们看一下打印台




他们两个的内存地址是一样的,也就是说指针指向的是同一块内存地址,所以你修改了A,B自然也会变,也就是所谓浅拷贝.


这时如果你删除你会发现,他们都消失了



想避免这种方法的话,我们就要对person类实现深拷贝,那么就要遵循NSCopying协议,代码如下

#import "PersonItem.h"

@implementation PersonItem

//遵守NSCopying协议,深复制出一个新的item
- (id)copyWithZone:(NSZone *)zone{
    
    PersonItem *item = [[[self class]allocWithZone:zone]init];
    
    item.headerImg = [_headerImg copy];
    item.age = _age;
    item.height = _height;
    item.weight = _weight;
    item.numID = _numID;
    item.classNum = _classNum;
    item.score0 = _score0;
    item.score1 = _score1;
    item.score2 = _score2;
    item.score3 = _score3;
    item.score4 = _score4;
    
    return item;
}

@end

这样如果你选择正确的复制的话,结果就会是这样的



原因自然是他们分别是2个不同的内存地址,所以修改互不干扰



这就是我理解的原型模式,如有错误,希望大家指正.



猜你喜欢

转载自blog.csdn.net/u010670117/article/details/48109025