前言

  • copy:需要先实现 NSCopying 协议,创建的是不可变副本。

  • mutableCopy:需要实现 NSMutableCopying 协议,创建的是可变副本。

  • 浅拷贝:指针拷贝,源对象和副本指向的是同一个对象。对象的引用计数器 +1,其实相当于做了一次 retain 操作。

  • 深拷贝:内容拷贝,源对象和副本指向的是两个不同的对象。源对象引用计数器不变,副本计数器设置为 1。

  • 只有不可变对象创建不可变副本(copy)才是浅拷贝,其它都是深拷贝。

  • 在 iOS 中并不是所有的对象都支持 copy,mutableCopy,遵守 NSCopying 协议的类可以发送 copy 消息,遵守 NSMutableCopying 协议的类才可以发送 mutableCopy 消息。假如发送了一个没有遵守上 诉两协议而发送 copy 或者 mutableCopy,那么就会发生异常。但是默认的 iOS 类并没有遵守这两个协议。如果想自定义一下 copy 那么就必须遵守 NSCopying,并且实现 copyWithZone: 方法,如果想自定义一下 mutableCopy 那么就必须遵守 NSMutableCopying,并且实现 mutableCopyWithZone: 方法。

  • copy 是创建一个新对象,retain 是创建一个指针,引用对象计数加 1。copy 属性表示两个对象内容相同,新的对象 retain 为 1 ,与旧有对象的引用计数无关,旧有对象没有变化。copy 减少对象对上下文的依赖。retain 属性表示两个对象地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的 retain 值 +1 也就是说,retain 是指针拷贝,copy 是内容拷贝。

1、系统的非容器类对象

  1. /*
  2. 系统的非容器类对象指的是 NSString,NSNumber 等等一类的对象。
  3. 对于系统的非容器类对象,如果对一不可变对象复制,copy 是指针复制(浅拷贝),mutableCopy 就是对象复制(深拷贝)。
  4. 如果是对可变对象复制,都是深拷贝,但是 copy 返回的对象是不可变的。
  5. */
  6. NSString *string = @"origion";
  7. NSString *stringCopy = [string copy];
  8. NSMutableString *mstringMCopy = [string mutableCopy];
  9. NSLog(@"%p", string);
  10. NSLog(@"%p", stringCopy); // 地址与 string 相同
  11. NSLog(@"%p", mstringMCopy); // 地址与 string 不同
  12. NSLog(@"%zi", [string retainCount]); // 引用计数为 2
  13. NSLog(@"%zi", [stringCopy retainCount]); // 引用计数为 2
  14. NSLog(@"%zi", [mstringMCopy retainCount]); // 引用计数为 1
  15. NSLog(@"%@", stringCopy); // 内容与 string 相同
  16. NSLog(@"%@", mstringMCopy); // 内容与 string 相同
  17. [mstringMCopy appendString:@"!!"]; // mstringMCopy 是可变的
  18. NSLog(@"%@", mstringMCopy);
  19. /*
  20. string 和 stringCopy 指向的是同一块内存区域(又叫 apple 弱引用 weak reference),此时 stringCopy 的
  21. 引用计数和 string 的一样都为2。而 mstringMCopy 则是我们所说的真正意义上的复制,系统为其分配了新内存,但指针
  22. 所指向的字符串还是和 string 所指的一样。
  23. */
  24. NSMutableString *mstring = [NSMutableString stringWithString: @"origion"];
  25. NSString *stringCopy = [mstring copy];
  26. NSMutableString *mStringCopy = [mstring copy];
  27. NSMutableString *mstringMCopy = [mstring mutableCopy];
  28. NSLog(@"%p", mstring);
  29. NSLog(@"%p", stringCopy); // 地址与 string 不同
  30. NSLog(@"%p", mStringCopy); // 地址与 string 不同,与 stringCopy 相同
  31. NSLog(@"%p", mstringMCopy); // 地址与 string 不同
  32. NSLog(@"%@", mstring);
  33. NSLog(@"%@", stringCopy); // 内容与 string 相同
  34. NSLog(@"%@", mStringCopy); // 内容与 string 相同
  35. NSLog(@"%@", mstringMCopy); // 内容与 string 相同
  36. [mstring appendString:@" origion!"];
  37. // [mStringCopy appendString:@"mm"]; // error,mStringCopy 不可变
  38. [mstringMCopy appendString:@"!!"]; // mstringMCopy 可变
  39. NSLog(@"%@", mstring);
  40. NSLog(@"%@", mstringMCopy);
  41. /*
  42. 以上四个 NSString 对象所分配的内存都是不一样的。但是对于 mStringCopy 其实是个不可变对象,所以上述会报错。
  43. */

2、系统的容器类对象

  1. /*
  2. 系统的容器类对象指 NSArray,NSDictionary 等。
  3. 对于容器类本身,上面讨论的结论也是适用的,需要探讨的是复制后容器内对象的变化。
  4. 对于系统的容器类对象,copy 返回不可变对象,是指针复制,包括里面的元素也是指向相同的指针。mutablecopy
  5. 返回可变对象,是对象复制,其中容器内的元素内容都是指针复制,可以改变其内的元素。
  6. */
  7. NSArray *array = [NSArray arrayWithObjects:@"a", @"b", @"c", nil];
  8. NSArray *arrayCopy = [array copy];
  9. NSMutableArray *mArrayMCopy = [array mutableCopy];
  10. NSLog(@"%p", array);
  11. NSLog(@"%p", arrayCopy); // 地址与 array 相同
  12. NSLog(@"%p", mArrayMCopy); // 地址与 array 不同
  13. NSLog(@"%zi",[array retainCount]);
  14. NSLog(@"%zi",[arrayCopy retainCount]);
  15. NSLog(@"%zi",[mArrayMCopy retainCount]);
  16. NSLog(@"%@", array);
  17. NSLog(@"%@", arrayCopy); // 内容与 array 相同
  18. NSLog(@"%@", mArrayMCopy); // 内容与 array 相同
  19. [mArrayMCopy addObject:@"de"]; // mArrayMCopy 可变
  20. NSLog(@"%@", array);
  21. NSLog(@"%@", mArrayMCopy);
  22. [mArrayMCopy removeObjectAtIndex:0];
  23. NSLog(@"%@", array);
  24. NSLog(@"%@", mArrayMCopy);
  25. /*
  26. copy 返回不可变对象,mutablecopy 返回可变对象,arrayCopy 是指针复制,而 mArrayMCopy 是对象复制。
  27. arrayCopy 是和 array 同一个 NSArray 对象(指向相同的对象),包括 array 里面的元素也是指向相同的指针。
  28. mArrayMCopy 是 array 的可变副本,指向的对象和 array 不同,但是其中的元素和 array 中的元素指向的是同一
  29. 个对象。mArrayMCopy 还可以改变其内的元素:删除或添加。但是注意的是,容器内的元素内容都是指针复制。
  30. */
  31. NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"], @"b", @"c", nil];
  32. NSArray *arrayCopy = [array copy];
  33. NSMutableArray *mArrayMCopy = [array mutableCopy];
  34. NSLog(@"%p", array);
  35. NSLog(@"%p", arrayCopy); // 地址与 array 相同
  36. NSLog(@"%p", mArrayMCopy); // 地址与 array 不同
  37. NSLog(@"%zi",[array retainCount]);
  38. NSLog(@"%zi",[arrayCopy retainCount]);
  39. NSLog(@"%zi",[mArrayMCopy retainCount]);
  40. NSLog(@"%@", array);
  41. NSLog(@"%@", arrayCopy); // 内容与 array 相同
  42. NSLog(@"%@", mArrayMCopy); // 内容与 array 相同
  43. NSMutableString *testString = [array objectAtIndex:0];
  44. [testString setString:@"1a1"]; // 这样会改变 testString 的指针,其实是将 @“1a1” 临时对象
  45. 赋给了 testString。这样以上三个数组的首元素都被改变了。
  46. NSLog(@"%@", array);
  47. NSLog(@"%@", arrayCopy); // 内容与 array 相同
  48. NSLog(@"%@", mArrayMCopy); // 内容与 array 相同
  49. /*
  50. arrayCopy,mArrayMCopy 和 array 指向的都是不一样的对象,但是其中的元素都是一样的对象,同一个指针。
  51. 对于容器而言,其元素对象始终是指针复制。如果需要元素对象也是对象复制,就需要实现深拷贝。
  52. */
  53. NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"], @"b", @"c", nil];
  54. NSArray *deepCopyArray = [[NSArray alloc] initWithArray:array copyItems: YES];
  55. // 使用归档/反归档拷贝
  56. NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array]];
  57. NSLog(@"%@", array);
  58. NSLog(@"%@", deepCopyArray); // 内容与 array 相同
  59. NSLog(@"%@", trueDeepCopyArray); // 内容与 array 相同
  60. NSMutableString *testString1 = [array objectAtIndex:0];
  61. [testString1 setString:@"1a1"];
  62. NSLog(@"%@", array);
  63. NSLog(@"%@", deepCopyArray); // 内容与 array 不同
  64. NSLog(@"%@", trueDeepCopyArray); // 内容与 array 不同
  65. /*
  66. trueDeepCopyArray 是完全意义上的深拷贝,而 deepCopyArray 则不是,对于 deepCopyArray 内的不可变元素其还是
  67. 指针复制。或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要
  68. 指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。举个例子,[[array objectAtIndex:0] appendstring:@”sd”]
  69. 后其他的容器内对象并不会受影响。[[array objectAtIndex:1] 和 [[deepCopyArray objectAtIndex:1] 尽管是指向同一块内存,
  70. 但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全意义上的深拷贝,但是 apple 的官方
  71. 文档将其列为 deep copy 了,并添加了 copy 和 mutablity 的关系说明。
  72. */

3、自定义对象

  • 需要自己要实现 NSCopying,NSMutableCopying 这样就能调用 copy 和 mutablecopy 了。
  1. // Teacher.h
  2. // 遵守 NSCopying, NSMutableCopying 协议
  3. @interface Teacher : NSObject <NSCopying, NSMutableCopying>
  4. @property (nonatomic, copy) NSString *name;
  5. @property (nonatomic, assign) int age;
  6. @end
  7. // Teacher.m
  8. @implementation Teacher
  9. // 实现协议方法,委托方会自动调用协议中的该方法
  10. - (id)copyWithZone:(NSZone *)zone{
  11. Teacher *copy = [[Teacher allocWithZone:zone] init];
  12. copy.name = self.name;
  13. copy.age = self.age;
  14. return copy;
  15. }
  16. // 实现协议方法,委托方会自动调用协议中的该方法
  17. - (id)mutableCopyWithZone:(NSZone *)zone{
  18. Teacher *copy = [[Teacher allocWithZone:zone] init];
  19. copy.name = self.name;
  20. copy.age = self.age;
  21. return copy;
  22. }
  23. - (NSString *)description{
  24. return [NSString stringWithFormat:@"%@ %i", self.name, self.age];
  25. }
  26. @end
  27. // main.m
  28. #import "Teacher.h"
  29. Teacher *tch = [[Teacher alloc] init];
  30. tch.name = @"xiao xin";
  31. tch.age = 28;
  32. // 内容复制
  33. Teacher *tchCpy = [tch copy];
  34. // 内容复制
  35. Teacher *tchMCopy = [tch mutableCopy];
  36. tchCpy.age = 20;
  37. tchMCopy.age = 22;
  38. NSLog(@"%p", tch);
  39. // tchCpy 与 tch 的地址不同
  40. NSLog(@"%p", tchCpy);
  41. // tchMCopy 与 tch 的地址不同
  42. NSLog(@"%p", tchMCopy);
  43. NSLog(@"%@", tch);
  44. // tchCpy 与 tch 的实例变量的值不同
  45. NSLog(@"%@", tchCpy);
  46. // tchMCopy 与 tch 的实例变量的值不同
  47. NSLog(@"%@", tchMCopy);
  48. // Children.h
  49. // 遵守 NSCopying, NSMutableCopying 协议
  50. @interface Children : NSObject <NSCopying, NSMutableCopying>
  51. @property (nonatomic, copy) NSString *name;
  52. @property (nonatomic, assign) int age;
  53. @end
  54. // Children.m
  55. @implementation Children
  56. // 实现协议方法,委托方会自动调用协议中的该方法。
  57. - (id)copyWithZone:(NSZone *)zone{
  58. // 若有类继承此类,并且此方法在子类中可用
  59. id copy = [[[self class] allocWithZone:zone] init];
  60. [copy setName:self.name];
  61. [copy setAge:self.age];
  62. return copy;
  63. }
  64. // 实现协议方法,委托方会自动调用协议中的该方法。
  65. - (id)mutableCopyWithZone:(NSZone *)zone{
  66. // 若有类继承此类,并且此方法在子类中可用
  67. id copy = [[[self class] allocWithZone:zone] init];
  68. [copy setName:self.name];
  69. [copy setAge:self.age];
  70. return copy;
  71. }
  72. @end
  73. // GoodChildren.h
  74. @interface GoodChildren : Children
  75. @property (nonatomic, assign)int score;
  76. @end
  77. // GoodChildren.m
  78. @implementation GoodChildren
  79. // 实现协议方法,委托方会自动调用协议中的该方法。
  80. - (id)copyWithZone:(NSZone *)zone{
  81. GoodChildren *copy = [super copyWithZone:zone];
  82. copy.score = self.score;
  83. return copy;
  84. }
  85. // 实现协议方法,委托方会自动调用协议中的该方法。
  86. - (id)mutableCopyWithZone:(NSZone *)zone{
  87. GoodChildren *copy = [super copyWithZone:zone];
  88. copy.score = self.score;
  89. return copy;
  90. }
  91. - (NSString *)description{
  92. return [NSString stringWithFormat:@"%@ %i %d", self.name, self.age, self.score];
  93. }
  94. @end
  95. // main.m
  96. #import "GoodChildren.h"
  97. GoodChildren *gChild = [[GoodChildren alloc] init];
  98. gChild.name = @"xiao xin";
  99. gChild.age = 18;
  100. gChild.score = 88;
  101. // 内容复制
  102. GoodChildren *gChildCopy = [gChild copy];
  103. // 内容复制
  104. GoodChildren *gChildMCopy = [gChild mutableCopy];
  105. gChildCopy.age = 20;
  106. gChildMCopy.age = 22;
  107. gChildCopy.score = 100;
  108. gChildMCopy.score = 120;
  109. NSLog(@"%p", gChild);
  110. // gChildCopy 与 gChild 的地址不同
  111. NSLog(@"%p", gChildCopy);
  112. // gChildMCopy 与 gChild 的地址不同
  113. NSLog(@"%p", gChildMCopy);
  114. NSLog(@"%@", gChild);
  115. // gChildCopy 与 gChild 的实例变量的值不同
  116. NSLog(@"%@", gChildCopy);
  117. // gChildMCopy 与 gChild 的实例变量的值不同
  118. NSLog(@"%@", gChildMCopy);

iOS - OC Copy 拷贝的更多相关文章

  1. iOS - OC 面向对象语法

    1.类 1)根类:因为类 NSObject 是层次结构的最顶层,因此称为根类. 可以将类称为子类(subclass)和父类(superclass),也可以将类称为子类和超类. 2)分类/类别(cate ...

  2. ios+oc面试题

    ios+oc面试题     浅复制和深复制的区别?//浅拷贝和深拷贝答案:浅层复制(copy):只复制指向对象的指针,而不复制引用对象本身.//通过对象的指针来访问这个对象深层复制(mutableCo ...

  3. OC copy mutableCopy, 浅拷贝,深拷贝

    copy与mutableCopy都是深拷贝,区别是mutableCopy拷贝出的对象是可变的. OC对象基本都是通过指针访问,所以一般情况下,通过对指针的赋值都是浅拷贝,即只是拷贝了一份对象的指针,对 ...

  4. iOS中copy和strong修饰符的区别

    iOS中copy和strong修饰符的区别 //用copys修饰的生成的都是不可变的对象 ,如果调用可变类型方法的直接报错 @property(nonatomic,copy)NSString * cp ...

  5. 夺命雷公狗—angularjs—23—copy拷贝对象

    copy这在angularjs中是一个拷贝对象的方法: <!DOCTYPE html> <html lang="en" ng-app="myapp&qu ...

  6. iOS OC语言原生开发的IM模块--RChat

    iOS OC语言原生开发的IM模块,用于项目中需要原生开发IM的情况,具备发送文字.表情.语音.图片.视频等完整功能,包含图片预览视频播放等功能,此项目将会长期更新如有问题可以提出,我的邮箱:fshm ...

  7. 【iOS】copy 关键字

    以前没注意过 iOS 的 copy, nonatomic, assign, weak, strong 等关键字. 偏偏今天遇到了一个问题,恰恰是关键字的问题,如图: 之前用的是 assign, 没有用 ...

  8. ios OC 关键字 copy,strong,weak,assign的区别

    一.先介绍 copy.strong.weak 的区别,如代码所示 @property(copy,nonatomic)NSMutableString*aCopyMStr; @property(stron ...

  9. iOS - OC RunTime 运行时

    1.运行时的使用 向分类中添加属性 // 包含运行时头文件 #import <objc/runtime.h> /* void objc_setAssociatedObject(id obj ...

随机推荐

  1. HDU 5876:Sparse Graph(BFS)

    http://acm.hdu.edu.cn/showproblem.php?pid=5876 Sparse Graph Problem Description   In graph theory, t ...

  2. 上传图片到阿里云OSS和获取上传图片的外网url的步骤

    啥都不说  直接上代码 1.html: <form action="/bcis/api/headImgUpload.json" method="post" ...

  3. [HTML]表格的一切

    如何设置HTML页面自适应宽度的table(表格): <table width="95%" border="1" cellpadding="2& ...

  4. python 运行python manege.py runserver时报错:“no module named djangorestframework” 的解决方案

    python 运行python manege.py runserver时报错:“no module named djangorestframework” 的解决方案 importerror:no mo ...

  5. Pearls

    Pearls Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 7980 Accepted: 3966 Description In ...

  6. Unity-Animator深入系列---状态机面板深入

    回到 Animator深入系列总目录 本篇不讲解所有的面板功能,只是针对一些非常用功能进行介绍. 1.状态 1.1状态简介 简单的不做介绍了,需要特别注意: 1.Paramter勾选后可以指定参数控制 ...

  7. JavaScript的构造器与对象(二)

    constructor 的用法:对象的构造函数  每一个函数的Prototype属性指向的对象都包含唯一一个不可枚举属性constructor,该属性的值是这么一个对象:它指向了它所在的构造函数. 语 ...

  8. grails-domain-id 无生成策略,由程序控制

    一 domain class 中标示 class Menu implements Comparable<Menu>{ String id; String name; } static co ...

  9. P2680 运输计划

    http://www.luogu.org/problem/show?pid=2680#sub 题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航 ...

  10. MTK6589下传感器框架结构和代码分析以及传感器的参数指标

    MTK6589下传感器框架结构和代码分析以及传感器的参数指标 作者:韩炜彬  中国当代著名嵌入式研究专家 一.      模块框架 1)配置 路径:Alps/mediatek/config/$(pro ...