"OC基础"这个分类的文章是我在自学Stephen G.Kochan的《Objective-C程序设计第6版》过程中的笔记。

1、浅复制即是指针赋值,复制对象的修改会影响被复制对象;深复制即是真正的对象复制,会复制出来一个副本,这个副本和原对象是完全分开的。

Foundation类实现了两个复制的方法:copy和mutableCopy。copy复制出来的是一个不可变的副本,mutableCopy复制出来的是一个可变的副本。

同时注意,产生一个对象的可变副本并不要求被复制的对象本身是可变的,同样的,产生一个不可变副本也不要求被复制对象本身是不可变的。

2、在iOS中,并不是所有的对象都支持copy和mutableCopy,只有遵守NSCopying协议的类可以发送copy消息,遵守NSMutableCopying协议的类才可以发送mutableCopy消息。但是默认的自定义类并没有遵守这两个协议。所以如果想自定义使用copy,那么就必须遵守NSCopying协议,并且实现copyWithZone:方法,如果想自定义使用mutableCopy,那么就必须遵守NSMutableCopying协议,并且实现mutableCopyWithZone:方法。

3、深复制和浅复制的操作:

iOS提供了copy和mutablecopy两个方法,顾名思义,copy方法复制出来的是一个不可变对象,而mutablecopy就是复制出来的就是一个mutable的对象。以下将分两种情况来说明:

(1)、系统的非容器类对象(即是指NSString、NSNumber等这一类的对象)情况1:

...

       NSString *s = @"abcd";

       NSString *sCopy = [s copy];

       NSMutableString *mutaSMutaCopy = [s mutableCopy];

       [mutaSMutaCopy appendString:@"ef"];

...

       在这种情况下,s和sCopy指向的是同一块内存区域,此时sCopy的引用计数和s一样都为2。而sMCopy则是我们所认为的真正意义上的复制,系统为其分配了新内存,但指针所指向的字符串还是和s所指的一样。

(2)、系统的非容器类对象情况2:

...

NSMutableString *s = [NSMutableString stringWithString: @"abcd"];

NSString *sCopy = [s copy];

NSMutableString *mutaSCopy = [s copy];  //Mutable S Copy,它其实是不可变的

NSMutableString *sMutaCopy = [s mutableCopy];

[mutaSCopy appendString:@"ef"];  //这句会产生错误,因为mSCopy其实不可变的

[s appendString:@"ef"];

[sMutaCopy appendString:@"gh"];

...

以上四个NSString对象所分配的内存都是不一样的。由于mutaSCopy其实是个不可变对象,所以会有上述的报错。所以对于系统的非容器类对象,我们可以认为:

对不可变对象做复制,copy是指针复制(浅复制),而mutableCopy就是对象复制(深复制);如果是对可变对象进行复制,就全部都是深复制,这种情况需要注意:使用copy复制返回的对象无论如何都会是不可变的。

(3)、系统的容器类对象 (即是指NSArray、NSDictionary类等)情况1:

对于容器类,上面的结论也是适用的,需要探讨的是复制后容器内对象的变化,看以下代码:

...

NSArray *a = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];

NSArray *aCopy = [a copy];

...

aCopy和a是同一个NSArray对象(指向相同的对象),包括array里面的元素也是指向相同的指针,所以只是指针复制。再看下一个语句:

...

NSMutableArray *aMCopy = [a mutableCopy];

...

aMCopy是a的可变副本,指向的对象就和a不同了,但是其中的元素和a中的元素指向的仍然是同一个对象。aMCopy还可以修改自己的对象:

...

[aMCopy addObject:@"de"];

[aMCopy removeObjectAtIndex: 0];

...

从上面的例子可以看出:aCopy是指针复制,而aMCopy是对象复制,aMCopy还可以删除或添加其元素。但是需要注意的是,容器内的元素的内容都是指针复制

(4)、系统的容器类对象情况2:

下面再看另一种情况:

...

NSArray *a = [NSArray arrayWithObjects: [NSMutableString stringWithString: @"a"], @"b", @"c", nil];

NSArray *aCopy = [a copy];

NSMutableArray *aMCopy = [a mutableCopy];

...

aCopy,aMCopy和a指向的都是不一样的对象,但是其中的元素都是一样的,因为元素内容存储的是指针,还是指向同一个位置。看以下代码:

...

NSMutableString *testString = [a objectAtIndex:0];

[testString appendString:@" tail"];

...

然后可以发现三个数组的首元素都被改变。注意,在这里进行赋值的时候,使用testString = @"tail"这样赋值是不行的,因为这样其实是改变了testString的指针指向,将@“tail”临时对象赋给了testString。

(5)、由此可见,对于容器而言,其元素对象始终是指针复制。如果需要元素对象也是对象复制,就需要实现深拷贝:

NSArray *array = [NSArray arrayWithObjects: [NSMutableString stringWithString: @"first"] ,[NSString stringWithString:@"b"], @"c", nil];

NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:

[NSKeyedArchiver archivedDataWithRootObject: array]];

trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则还不是,对于deepCopyArray内的不可变元素,deepCopyArray使用的还是指针复制。

因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。这种情况下,哪怕你对容器内的元素重新赋值,也不会影响到其他的容器。举个例子,使用[array objectAtIndex: 0] = @”sd”之后,其他的容器内对象并不会受影响。

[array objectAtIndex: 1]和[deepCopyArray objectAtIndex: 0]尽管是指向同一块内存,但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以虽然这并不是完全意义上的深拷贝,但是apple的官方文档将其列为deep copy了。

4、使用Foundation类的copy或mutableCopy方法是,深复制并不是默认执行的。从3中也可以看出,对不可变对象使用copy的效果也仅仅是指针复制。

5、关于实现自定义类的copy方法:

(1)、首先自定义类必须是Foundation类的子类;

(2)、要为自定义类实现copy方法,必须先实现<NSCopying>协议,再重载copyWithZone方法;要为自定义类实现mutableCopy方法,必须先实现<NSMutableCopying>协议,然后重载mutableCopyWithZone方法;因为自定义类的copy和mutableCopy是通过调用copyWithZone和mutableCopyWithZone来实现的;

(3)、以自定义分数类Fraction类为例,它含有两个实例变量:分母d和分子n,要实现这个类的copy方法,首先应该在@interface部分引入<NSCopying>协议:

@interface Fraction: NSObject <NSCopying>

然后在@implementation部分添加以下内容来重载copyWithZone方法:

- (id) copyWithZone: (NSZone *) zone {

 id newFraction = [[[self class] allocWithZone] init]; 

         //这么写是考虑到可能有子类继承会这个方法

[newFraction setTo: n over: d];   //使用原有的设值方法

return newFraction;

}

(4)、如果父类本身已实现了copy或者mutableCopy方法,那么在重载的过程中应先调用父类的copy方法,再添加附加的内容;

6、在合成设置属性的时候,如果使用以下语句合成属性:

@property (nonatomic, copy) NSString *name;

...

@synthesize name;

...

那么编译器自动生成的setter方法是:

-(void) setName: (NSString *) theName {

if(theName != name)

name = [theName copy];

}

其实也就是使用了copy方法了。

7、属性没有mutableCopy特性。即使是可变的实例变量也是使用copy方法去复制,所以都会生成一个对象的不可变副本。

8、在copy方法中,如果正在复制的实例变量包含不可变的对象(比如不可变的字符串对象),那么系统其实并没有生成这个对象内容的副本。仅仅生成一个对于这个对象的新引用,就足够了,因为反正它也是不可变的,不可能会有改动副本影响到原对象这种情况发生。

OC基础16:复制对象的更多相关文章

  1. ios开发之OC基础-类和对象

    本系列的文章主要来自于个人在学习前锋教育-欧阳坚老师的iOS开发教程之OC语言教学视频所做的笔记,边看视频,边记录课程知识点.建议大家先过一遍视频,在看视频的过程中记录知识点关键字,把把握重点,然后再 ...

  2. OC基础:类和对象 分类: ios学习 OC 2015-06-12 18:55 17人阅读 评论(0) 收藏

    OC:Objective-c     面向对象的c语言,简称obj-c或者OC OC和C的区别 1.OC是C语言的超集,OC是在C语言的基础上结合smalltalk的优点,开发出来的语言.oc兼容所有 ...

  3. OC基础(16)

    autorelease基本使用 autorelease注意事项 *:first-child { margin-top: 0 !important; } body > *:last-child { ...

  4. 二.OC基础--1,对象的存储细节,2,#pragma mark指令,3,函数和对象方法的区别,4,对象和方法之间的关系 ,5.课堂习题

    1,对象的存储细节, 1. 当创建一个对象的时候:Person *p1 = [Person new],做了三件事情: 1,申请堆内存空间: 2,给实例变量初始化: 3,返回所申请空间的首地址; 2. ...

  5. OC基础笔记目录

    OC基础(1) Objective-C简介 OC和C对比 第一个OC程序 面向对象思想 OC基础(2) 类与对象 类的设计 第一个OC类 对象方法的声明和实现 类方法的声明和实现 OC基础(3) 对象 ...

  6. 五.OC基础--1.多态,2.类对象,3.点语法,4.@property&@synthesize,5.动态类型,内省(判断对象是否遵循特定的协议,以及是否可以响应特定的消息)

    五.OC基础--1.多态, 1. 多态概念,定义:多态就是某一类事物的多种形态: 表现形式: Animal *ani = [Dog new]; 多态条件:1.有继承关系 2.有方法的重写 2.多态代码 ...

  7. PHP基础知识之对象复制

    对象的复制默认为浅复制 进行深复制的方法为:在类中定义魔法方法__clone(),类的对象复制时,会自动调用 __clone方法,在 __clone方法中可以进行各种复制对象的个性化 class My ...

  8. OC学习16——对象归档

    转载自  OC学习篇之---归档和解挡 OC中的归档就是将对象写入到一个文件中,Java中的ObjectInputStream和ObjectOutputStream来进行操作的.当然在操作的这些对象都 ...

  9. OC基础12:数字、字符串和集合1

    "OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1.有时要将一些数字数据类型的值当做对象来 ...

随机推荐

  1. UVA 712-S-Trees(满二叉树的简单查询)

    题意:给一棵满二叉树,叶子节点赋予权值,0或者1,对于每个查询输出叶子节点的权值,每个查询0代表往左走,1代表往右走,这题坑的地方是层的访问顺序,如第二组测试,由上到下依次是x3,x1,x2,假如给一 ...

  2. Code Generation and T4 Text Templates

    Code Generation and T4 Text Templates Code Generation and T4 Text Templates

  3. ssl相关

    http://www.willrey.com/support/ssl_DES.html http://www.07net01.com/linux/Linuxdejiamirenzhenggongnen ...

  4. Jekins安装

    1. Jekins支持多系统:windows, mac,linux 2. Jekins安装方式有三种:直接war文件安装,安装包安装,将war文件放到web容器安装 3. 在windows下安装 a. ...

  5. crm使用url打开窗口视图

    //URL可寻址元素使您能够包含指向Microsoft Dynamics CRM窗口. 视图. 对话框和其它应用程序中的报告. //这样.您就能够轻松扩展其它应用程序.报表或站点,以便用户无需切换应用 ...

  6. Java程序猿的JavaScript学习笔记(汇总文件夹)

    最终完结了,历时半个月. 内容包含: JavaScript面向对象特性分析,JavaScript高手必经之路. jQuery源代码级解析. jQuery EasyUI源代码级解析. Java程序猿的J ...

  7. 设置Android设备在睡眠期间始终保持WLAN开启的代码实现

    MainActivity例如以下: package cc.ab; import android.os.Bundle; import android.provider.Settings; import ...

  8. QT笔记(1)--QT编程环境搭建

    一.QT简介 Qt  是一个1991年由奇趣科技开发的跨平台C++图形用户界面应用程序开发框架.它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器.Qt是面向对象的框架,使用特殊 ...

  9. QUdpSocket Class

    翻译自:QT官网文档QUdpSocket类 QUdpSocket类提供一个UDP套接字. Header: #include <QUdpSocket> qmake: QT += networ ...

  10. jsp页面使用jstl标签格式化String类型日期

    1.引入jstl <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> ...