"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. 常用数据结构及复杂度 array、LinkedList、List、Stack、Queue、Dictionary、SortedDictionary、HashSet、SortedSet

    原文地址:http://www.cnblogs.com/gaochundong/p/data_structures_and_asymptotic_analysis.html  常用数据结构的时间复杂度 ...

  2. libcurl 多线程使用注意事项 - Balder~专栏 - 博客频道 - CSDN.NET

    libcurl 多线程使用注意事项 - Balder~专栏 - 博客频道 - CSDN.NET libcurl 多线程使用注意事项 分类: C/C++学习 2012-05-24 18:48 2843人 ...

  3. MySql 取一天的开始时间和结束时间

    -- 取一开的开始时间 SELECT str_to_date(DATE_FORMAT(NOW(),'%Y-%m-%d'),'%Y-%m-%d %H:%i:%s'); -- 取第二天的开始时间 sele ...

  4. 很少人知道的office专用卸载工具

    Microsoft Office是微软公司开发的一套基于 Windows 操作系统的办公软件套装.常用组件有 Word.Excel.Powerpoint等.当我们不需要再用了或者想安装旧版本的话,首先 ...

  5. iOS开发-21UINavigationController导航控制器初始化 导航控制器栈的push和pop跳转理解

    (1)导航控制器初始化的时候一般都有一个根视图控制器,导航控制器相当于一个栈,里面装的是视图控制器,最先进去的在最下面,最后进去的在最上面.在最上面的那个视图控制器的视图就是这个导航控制器对外展示的界 ...

  6. 多点触控插件Hammer.js

    插件描述:Hammer.js是一个开源的,轻量级的javascript库,它可以在不需要依赖其他东西的情况下识别触摸,鼠标事件. 使用方法: <script src=<span class ...

  7. ff与ie 的关于js兼容性

    FF的FIREBUG,不仅能测试JS还能检查CSS错误,是一般常用的.但它主要检查FF方面的错误,对IE就无能为力了.要测试IE,就用ieTester,它可以测试IE几乎所有版本(1.0恐怕也用不到测 ...

  8. 【转换模型+扫描线】【UVA1398】Meteor

    The famous Korean internet company nhn has provided an internet-based photo service which allows The ...

  9. CMD下用csc.exe编译.cs 代码

    用惯了VS来写C#代码,要是用记事本写会不会觉得很好玩,然后再CMD窗口下编译运行,当然写一些简单的Console代码还是可以这么玩玩的,如果你觉得打开VS太麻烦的话. 把后缀名改成.cs,test. ...

  10. BeeswaxException 以及其他问题

    Could not read table BeeswaxException(handle=QueryHandle(log_context='ae18ae74-518f-400b-b4b0-d399ed ...