1. 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另一个可定制的对象,而且不需知道任何创建的细节。

比如说,有一个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

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]; }

现在有这样的一个需求,有一个人,也叫张三,也只有李四一个好友,如果不用原型模式,就会使下面的代码:

- (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];
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是最好的办法,既隐藏了对象创建的细节,对性能也是有着显著提高的,最主要的一点,就是没必要去重复写垃圾代码,浪费大量时间。

2. 浅拷贝(copy)与深拷贝(mutableCopy)

copy与mutableCopy的区别,如果学过c或者c++的朋友会知道指针这样一个概念,就是在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存

而深拷贝就是不仅增加了一个指针,并且还申请了一个新的内存,是这个新增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误。

上面的这句代码:

      copy->_friends = [_friends mutableCopy];

使用了->语法,因为_friend并非属性,而是在内部使用的实例变量。这里有一个问题,为什么要拷贝_friend实例变量呢?不拷贝这个变量,直接让两个对象共享同一个可变的set是否更简单?

如果这么做了,那么再给personOne添加一个新的朋友之后,拷贝过来的那个对象,也就是personThree也会“神奇”的与之为朋友了。在我上面写的那个实例中,这不是我想要的效果。然而,如果那个set是不可变的,那么就无需复制,因为其中的内容不可能会改变,所以就不用担心会出现上面的问题。如果复制了,那么内存中会有两个一模一样的set,造成了浪费。

引申出这样一个问题(面试题):怎样使用copy关键字?

以前我是这么回答的:

一般使用retain或者strong修饰属性时,是使引用对象的指针指向同一个对象,即为同一块内存地址。只要其中有一个指针变量被修改时所有其他引用该对象的变量都会被改变。
而使用copy关键字修饰在赋值时是释放旧对象,拷贝新对象内容。重新分配了内存地址。以后该指针变量被修改时就不会影响旧对象的内容了。
copy只有实现NSCopying协议的对象类型才有效。
常用于NSString和Block。

有一定错误,应该这样修正:

一般使用retain或者strong修饰属性时,是使引用对象的指针指向同一个对象,即为同一块内存地址。只要其中有一个指针变量被修改时所有其他引用该对象的变量都会被改变。

而copy关键字修饰时,如果新的对象是不可变的,那么它是直接引用新对象的内存地址,并不重新分配内存地址,如果新对象是可变的,那么在赋值时是释放旧对象,拷贝新对象内容。重新分配了内存地址。以后该指针变量被修改时就不会影响旧对象的内容了。

copy只有实现NSCopying协议的对象类型才有效。
常用于NSString和Block。

设计模式之原型模式(深入理解OC中的NSCopying协议以及浅拷贝、深拷贝)的更多相关文章

  1. c# 24种设计模式5原型模式(Prototype)

    前言 原型模式其实C# Object中已经提供了一个Clone( )方法,平时很少用到,最近读Retrofit源码时候看到有这种使用方式. 定义 原型模式就是在系统clone()标记的基础上,对Clo ...

  2. swift设计模式学习 - 原型模式

    移动端访问不佳,请访问我的个人博客 设计模式学习的demo地址,欢迎大家学习交流 原型模式 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 定义 用原型实例指定创建对象的种类,并且通 ...

  3. 设计模式_11_原型模式(prototype)深拷贝、浅拷贝

    设计模式_11_原型模式(prototype) 浅拷贝: package designPatternOf23; /** * 定义:用原型实例,指定创建对象的种类,并通过拷贝这些原型创建新的对象 * P ...

  4. C#设计模式(6)——原型模式(Prototype Pattern)

    一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在 ...

  5. iOS设计模式之原型模式

    原型模式 基本理解 原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节 ...

  6. C#设计模式之六原型模式(Prototype)【创建型】

    一.引言 在开始今天的文章之前先说明一点,欢迎大家来指正.很多人说原型设计模式会节省机器内存,他们说是拷贝出来的对象,这些对象其实都是原型的复制,不会使用内存.我认为这是不对的,因为拷贝出来的每一个对 ...

  7. C#设计模式之五原型模式(Prototype Pattern)【创建型】

    一.引言 在开始今天的文章之前先说明一点,欢迎大家来指正.很多人说原型设计模式会节省机器内存,他们说是拷贝出来的对象,这些对象其实都是原型的复制,不会使用内存.我认为这是不对的,因为拷贝出来的每一个对 ...

  8. C#设计模式(6)——原型模式(Prototype Pattern)(转)

    一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在 ...

  9. C#设计模式(6)——原型模式(Prototype Pattern) C# 深浅复制 MemberwiseClone

    C#设计模式(6)——原型模式(Prototype Pattern)   一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创 ...

随机推荐

  1. 树莓派进阶之路 (028) - 树莓派SQLite3的安装

    MySQL占用内存太大,而SQLite是一款轻量级零配置数据库,非常适合在树莓派和其他嵌入式系统中使用.SQLite文档详细资料丰富,本文不会详细解释SQLite数据库操作的方方面面,只能结合具体场景 ...

  2. idea上activiti插件的安装及使用

    最近做的东西需要用到activiti,做个笔记 首先下载activti插件- actiBPM File - settings-plugins-Browse-Repositories 搜索 actiBP ...

  3. springboot(六):如何优雅的使用mybatis

    这两天启动了一个新项目因为项目组成员一直都使用的是mybatis,虽然个人比较喜欢jpa这种极简的模式,但是为了项目保持统一性技术选型还是定了 mybatis.到网上找了一下关于spring boot ...

  4. android异步处理机制

    昨天面试被提问android的异步处理机制有哪些,他说处理new thread还有哪种方式,我说implement runnable,他说不是,比如intentservice. 我说那还有asyncT ...

  5. appium简明教程(7)——Desired Capabilities详解

    Desired Capabilities在启动session的时候是必须提供的. Desired Capabilities本质上是key value的对象,它告诉appium server这样一些事情 ...

  6. [转]PostgreSQL 逻辑结构 和 权限体系 介绍

    摘要: 本文旨在帮助用户理解PostgreSQL的逻辑结构和权限体系,帮助用户快速的理解和管理数据库的权限. 逻辑结构 最上层是实例,实例中允许创建多个数据库,每个数据库中可以创建多个schema,每 ...

  7. Python MySQLdb 批量插入 封装

    def insert_data_many(dbName,list_data_dict): try: # 得到列表的第一个字典集合 data_dict = list_data_dict[0] # 得到( ...

  8. windows 环境使用 kafka

    近来学习 kafka,网上搜的教程好多不好用.在此开一贴记录一下学习过程.推荐官网,是最好的教程 http://kafka.apache.org/quickstart 官网上是linux 环境,我用的 ...

  9. hive元数据研究

    hive的元数据存放在关系型数据库中,元数据中存储了hive中所有表格的信息,包括表格的名字,表格的字段,字段的类型,注释.这些信息分散的存放在各个表中,给定一个hive中的表格名字,查询这个表中含有 ...

  10. idea自动编译

    idea修改后台代码自动编译 On 'update' action = Update classes and resources On frame deactivation = Update clas ...