"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. Android消息机制不完全解析(上)

        Handler和Message是Android开发者常用的两个API,我一直对于它的内部实现比较好奇,所以用空闲的时间,阅读了一下他们的源码.    相关的Java Class: androi ...

  2. Python的ASCII, GB2312, Unicode , UTF-8 相互转换

    ASCII 是一种字符集,包括大小写的英文字母.数字.控制字符等,它用一个字节表示,范围是 0-127 Unicode分为UTF-8和UTF-16.UTF-8变长度的,最多 6 个字节,小于 127 ...

  3. Python 自动化脚本学习(三)

    函数 例子 def hello(): print("hello" + "world"); 有参数的函数 def hello(name): print(" ...

  4. IOS 快速排序法

    - (NSMutableArray *)QuickSort:(NSMutableArray *)list StartIndex:(NSInteger)startIndex EndIndex:(NSIn ...

  5. 【求出所有最短路+最小割】【多校第一场】【G题】

    题意 A从1要追在N的 B 只能走最短的路 问B最少切断多少条路可以让A不能过来 问B最多切断多少条路A还是能过来 对于1 求出1到N的所有最短路的路径,对其求最小割 对于2 求出长度最小的最短路即可 ...

  6. O the joy of having nothing / 아무것도 갖지않고

    Chords: C G Am Em F C Dm G C G Am Em F C Am Dm G English - O the joy of having nothing and being not ...

  7. 使用xib方式创建UITableViewCell,设置Label自动换行注意事项

    自定义的UITableViewCell,使用xib方式创建,想要其中的UILabel换行显示:计算Label的高度,让其自动换行,总是没有效果. 我猜测原因可能在于使用了autolayout布局.只要 ...

  8. HDU 5806 - NanoApe Loves Sequence Ⅱ (BestCoder Round #86)

    若 [i, j] 满足, 则 [i, j+1], [i, j+2]...[i,n]均满足 故设当前区间里个数为size, 对于每个 i ,找到刚满足 size == k 的 [i, j], ans + ...

  9. u-boot基本命令

    1.查看环境变量 printenv 2.网络相关命令 设置开发板ip:setenv ipaddr 192.168.2.110 设置子网掩码:setenv netmask 255.255.255.0 设 ...

  10. CDZSC_2015寒假新人(1)——基础 f

    Description An inch worm is at the bottom of a well n inches deep. It has enough energy to climb u i ...