前言

  • 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、系统的非容器类对象

/*
系统的非容器类对象指的是 NSString,NSNumber 等等一类的对象。
对于系统的非容器类对象,如果对一不可变对象复制,copy 是指针复制(浅拷贝),mutableCopy 就是对象复制(深拷贝)。
如果是对可变对象复制,都是深拷贝,但是 copy 返回的对象是不可变的。
*/
NSString *string = @"origion";
NSString *stringCopy = [string copy];
NSMutableString *mstringMCopy = [string mutableCopy]; NSLog(@"%p", string);
NSLog(@"%p", stringCopy); // 地址与 string 相同
NSLog(@"%p", mstringMCopy); // 地址与 string 不同
NSLog(@"%zi", [string retainCount]); // 引用计数为 2
NSLog(@"%zi", [stringCopy retainCount]); // 引用计数为 2
NSLog(@"%zi", [mstringMCopy retainCount]); // 引用计数为 1
NSLog(@"%@", stringCopy); // 内容与 string 相同
NSLog(@"%@", mstringMCopy); // 内容与 string 相同
[mstringMCopy appendString:@"!!"]; // mstringMCopy 是可变的
NSLog(@"%@", mstringMCopy); /*
string 和 stringCopy 指向的是同一块内存区域(又叫 apple 弱引用 weak reference),此时 stringCopy 的
引用计数和 string 的一样都为2。而 mstringMCopy 则是我们所说的真正意义上的复制,系统为其分配了新内存,但指针
所指向的字符串还是和 string 所指的一样。
*/
NSMutableString *mstring = [NSMutableString stringWithString: @"origion"];
NSString *stringCopy = [mstring copy];
NSMutableString *mStringCopy = [mstring copy];
NSMutableString *mstringMCopy = [mstring mutableCopy]; NSLog(@"%p", mstring);
NSLog(@"%p", stringCopy); // 地址与 string 不同
NSLog(@"%p", mStringCopy); // 地址与 string 不同,与 stringCopy 相同
NSLog(@"%p", mstringMCopy); // 地址与 string 不同
NSLog(@"%@", mstring);
NSLog(@"%@", stringCopy); // 内容与 string 相同
NSLog(@"%@", mStringCopy); // 内容与 string 相同
NSLog(@"%@", mstringMCopy); // 内容与 string 相同
[mstring appendString:@" origion!"];
// [mStringCopy appendString:@"mm"]; // error,mStringCopy 不可变
[mstringMCopy appendString:@"!!"]; // mstringMCopy 可变
NSLog(@"%@", mstring);
NSLog(@"%@", mstringMCopy);
/*
以上四个 NSString 对象所分配的内存都是不一样的。但是对于 mStringCopy 其实是个不可变对象,所以上述会报错。
*/

2、系统的容器类对象

/*
系统的容器类对象指 NSArray,NSDictionary 等。
对于容器类本身,上面讨论的结论也是适用的,需要探讨的是复制后容器内对象的变化。
对于系统的容器类对象,copy 返回不可变对象,是指针复制,包括里面的元素也是指向相同的指针。mutablecopy
返回可变对象,是对象复制,其中容器内的元素内容都是指针复制,可以改变其内的元素。
*/
NSArray *array = [NSArray arrayWithObjects:@"a", @"b", @"c", nil];
NSArray *arrayCopy = [array copy];
NSMutableArray *mArrayMCopy = [array mutableCopy]; NSLog(@"%p", array);
NSLog(@"%p", arrayCopy); // 地址与 array 相同
NSLog(@"%p", mArrayMCopy); // 地址与 array 不同
NSLog(@"%zi",[array retainCount]);
NSLog(@"%zi",[arrayCopy retainCount]);
NSLog(@"%zi",[mArrayMCopy retainCount]);
NSLog(@"%@", array);
NSLog(@"%@", arrayCopy); // 内容与 array 相同
NSLog(@"%@", mArrayMCopy); // 内容与 array 相同
[mArrayMCopy addObject:@"de"]; // mArrayMCopy 可变
NSLog(@"%@", array);
NSLog(@"%@", mArrayMCopy);
[mArrayMCopy removeObjectAtIndex:0];
NSLog(@"%@", array);
NSLog(@"%@", mArrayMCopy);
/*
copy 返回不可变对象,mutablecopy 返回可变对象,arrayCopy 是指针复制,而 mArrayMCopy 是对象复制。
arrayCopy 是和 array 同一个 NSArray 对象(指向相同的对象),包括 array 里面的元素也是指向相同的指针。
mArrayMCopy 是 array 的可变副本,指向的对象和 array 不同,但是其中的元素和 array 中的元素指向的是同一
个对象。mArrayMCopy 还可以改变其内的元素:删除或添加。但是注意的是,容器内的元素内容都是指针复制。
*/ NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"], @"b", @"c", nil];
NSArray *arrayCopy = [array copy];
NSMutableArray *mArrayMCopy = [array mutableCopy]; NSLog(@"%p", array);
NSLog(@"%p", arrayCopy); // 地址与 array 相同
NSLog(@"%p", mArrayMCopy); // 地址与 array 不同
NSLog(@"%zi",[array retainCount]);
NSLog(@"%zi",[arrayCopy retainCount]);
NSLog(@"%zi",[mArrayMCopy retainCount]);
NSLog(@"%@", array);
NSLog(@"%@", arrayCopy); // 内容与 array 相同
NSLog(@"%@", mArrayMCopy); // 内容与 array 相同
NSMutableString *testString = [array objectAtIndex:0];
[testString setString:@"1a1"]; // 这样会改变 testString 的指针,其实是将 @“1a1” 临时对象
赋给了 testString。这样以上三个数组的首元素都被改变了。
NSLog(@"%@", array);
NSLog(@"%@", arrayCopy); // 内容与 array 相同
NSLog(@"%@", mArrayMCopy); // 内容与 array 相同
/*
arrayCopy,mArrayMCopy 和 array 指向的都是不一样的对象,但是其中的元素都是一样的对象,同一个指针。
对于容器而言,其元素对象始终是指针复制。如果需要元素对象也是对象复制,就需要实现深拷贝。
*/
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"], @"b", @"c", nil];
NSArray *deepCopyArray = [[NSArray alloc] initWithArray:array copyItems: YES];
// 使用归档/反归档拷贝
NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array]];
NSLog(@"%@", array);
NSLog(@"%@", deepCopyArray); // 内容与 array 相同
NSLog(@"%@", trueDeepCopyArray); // 内容与 array 相同
NSMutableString *testString1 = [array objectAtIndex:0];
[testString1 setString:@"1a1"];
NSLog(@"%@", array);
NSLog(@"%@", deepCopyArray); // 内容与 array 不同
NSLog(@"%@", trueDeepCopyArray); // 内容与 array 不同
/*
trueDeepCopyArray 是完全意义上的深拷贝,而 deepCopyArray 则不是,对于 deepCopyArray 内的不可变元素其还是
指针复制。或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要
指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。举个例子,[[array objectAtIndex:0] appendstring:@”sd”]
后其他的容器内对象并不会受影响。[[array objectAtIndex:1] 和 [[deepCopyArray objectAtIndex:1] 尽管是指向同一块内存,
但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全意义上的深拷贝,但是 apple 的官方
文档将其列为 deep copy 了,并添加了 copy 和 mutablity 的关系说明。
*/

3、自定义对象

  • 需要自己要实现 NSCopying,NSMutableCopying 这样就能调用 copy 和 mutablecopy 了。
// Teacher.h
// 遵守 NSCopying, NSMutableCopying 协议
@interface Teacher : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
// Teacher.m
@implementation Teacher
// 实现协议方法,委托方会自动调用协议中的该方法
- (id)copyWithZone:(NSZone *)zone{
Teacher *copy = [[Teacher allocWithZone:zone] init];
copy.name = self.name;
copy.age = self.age;
return copy;
}
// 实现协议方法,委托方会自动调用协议中的该方法
- (id)mutableCopyWithZone:(NSZone *)zone{
Teacher *copy = [[Teacher allocWithZone:zone] init];
copy.name = self.name;
copy.age = self.age;
return copy;
}
- (NSString *)description{
return [NSString stringWithFormat:@"%@ %i", self.name, self.age];
}
@end
// main.m
#import "Teacher.h"
Teacher *tch = [[Teacher alloc] init];
tch.name = @"xiao xin";
tch.age = 28;
// 内容复制
Teacher *tchCpy = [tch copy];
// 内容复制
Teacher *tchMCopy = [tch mutableCopy];
tchCpy.age = 20;
tchMCopy.age = 22;
NSLog(@"%p", tch);
// tchCpy 与 tch 的地址不同
NSLog(@"%p", tchCpy);
// tchMCopy 与 tch 的地址不同
NSLog(@"%p", tchMCopy);
NSLog(@"%@", tch);
// tchCpy 与 tch 的实例变量的值不同
NSLog(@"%@", tchCpy);
// tchMCopy 与 tch 的实例变量的值不同
NSLog(@"%@", tchMCopy);
// Children.h
// 遵守 NSCopying, NSMutableCopying 协议
@interface Children : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
// Children.m
@implementation Children
// 实现协议方法,委托方会自动调用协议中的该方法。
- (id)copyWithZone:(NSZone *)zone{
// 若有类继承此类,并且此方法在子类中可用
id copy = [[[self class] allocWithZone:zone] init];
[copy setName:self.name];
[copy setAge:self.age];
return copy;
}
// 实现协议方法,委托方会自动调用协议中的该方法。
- (id)mutableCopyWithZone:(NSZone *)zone{
// 若有类继承此类,并且此方法在子类中可用
id copy = [[[self class] allocWithZone:zone] init];
[copy setName:self.name];
[copy setAge:self.age];
return copy;
}
@end
// GoodChildren.h
@interface GoodChildren : Children
@property (nonatomic, assign)int score;
@end
// GoodChildren.m
@implementation GoodChildren
// 实现协议方法,委托方会自动调用协议中的该方法。
- (id)copyWithZone:(NSZone *)zone{
GoodChildren *copy = [super copyWithZone:zone];
copy.score = self.score;
return copy;
}
// 实现协议方法,委托方会自动调用协议中的该方法。
- (id)mutableCopyWithZone:(NSZone *)zone{
GoodChildren *copy = [super copyWithZone:zone];
copy.score = self.score;
return copy;
}
- (NSString *)description{
return [NSString stringWithFormat:@"%@ %i %d", self.name, self.age, self.score];
}
@end
// main.m
#import "GoodChildren.h"
GoodChildren *gChild = [[GoodChildren alloc] init];
gChild.name = @"xiao xin";
gChild.age = 18;
gChild.score = 88;
// 内容复制
GoodChildren *gChildCopy = [gChild copy];
// 内容复制
GoodChildren *gChildMCopy = [gChild mutableCopy];
gChildCopy.age = 20;
gChildMCopy.age = 22;
gChildCopy.score = 100;
gChildMCopy.score = 120;
NSLog(@"%p", gChild);
// gChildCopy 与 gChild 的地址不同
NSLog(@"%p", gChildCopy);
// gChildMCopy 与 gChild 的地址不同
NSLog(@"%p", gChildMCopy);
NSLog(@"%@", gChild);
// gChildCopy 与 gChild 的实例变量的值不同
NSLog(@"%@", gChildCopy);
// gChildMCopy 与 gChild 的实例变量的值不同
NSLog(@"%@", gChildMCopy);

Copy拷贝的更多相关文章

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

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

  2. iOS - OC Copy 拷贝

    前言 copy:需要先实现 NSCopying 协议,创建的是不可变副本. mutableCopy:需要实现 NSMutableCopying 协议,创建的是可变副本. 浅拷贝:指针拷贝,源对象和副本 ...

  3. 使用SCP命令在多个linux系统间进行copy拷贝,上传,下载...

    一,什么是scp scp是linux系统下基于ssh登陆进行安全的远程文件拷贝命令.scp命令可以在linux服务器之间复制文件和目录.scp使用ssh安全协议传输数据,具有和ssh一样的验证机制,从 ...

  4. python(41):copy拷贝(深拷贝deepcopy与浅拷贝copy)

    Python中的对象之间赋值时是按引用传递的,如果需要拷贝对象,需要使用标准库中的copy模块. 1.copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象. 2.copy.deepco ...

  5. GO 语言使用copy 拷贝切片的问题

    使用copy,直接改变原片的值,而不是先创建一个副本.

  6. 深浅拷贝的应用-copy、mutableCopy

    ViewController.h #import <UIKit/UIKit.h> @interface ViewController : UIViewController //如果想让li ...

  7. python 数据的拷贝

    # -*- config=utf-8 -*- #数据的拷贝 a=[1,2,3,4,5,6,"a","C"]; b=a;# a 与 b 的地址空间相同 a.app ...

  8. Docker Dockerfile COPY vs ADD

    http://blog.163.com/digoal@126/blog/static/163877040201410341236664/   在Dockerfile中, 我们可以使用ADD和COPY拷 ...

  9. ios retain 与 copy 的区别

    .retain 与copy区别 retain 的仅仅是引用计数加1,但是并没有创建新的对象.它们的指针是指向相同的内存地址. copy 是创建一个新的对象作为原来对象的副本,新创建出来的引用计数并没有 ...

随机推荐

  1. java流类练习前篇

    总结: package com.aini; import java.io.*; public class gf { public static String main(String[] args) t ...

  2. kubernetes 学习 ingress

    ingress是Kubernetes 暴露服务的一种方式. Ingress由两部分组成:Ingress Controller 和 Ingress 服务.     Ingress Contronler ...

  3. Xcode的Refactor使用

    最近在看<重构>的书,想到Xcode有一个Refactor的功能,不知道您用的多不多,用这个功能在我们开发过程中,可以提高开发效率. Refactor 右键显示 Refactor 一.Re ...

  4. 人脑和CPU

    人类的数学运算没有计算机快是因为神经信号速度没有电信号快吗,电信号是光速吧. 不过人类的cpu大脑和存储硬盘和内存超过目前计算机n条街,虽然传输速度慢,但是传输量也是大的,其实计算机就是根据人脑设计的 ...

  5. 2016.2.28 DataTable用法汇总

    将控件的DataSource转换为DataTable,但是,此控件的DataSource绑定时必须是DataTable,不能是List DataTable dt = (bgvRoutePortion. ...

  6. 2015.3.7 Dll CString不能作为传入参数而要用char*

    extern "C" __declspec(dllexport) void CalcArc_2(Point2D& pm, double am, double an, CSt ...

  7. 如何深度优化MySQL内核

    MYSQL数据库适用场景广泛,相较于Oracle.DB2性价比更高,Web网站.日志系统.数据仓库等场景都有MYSQL用武之地,但是也存在对于事务性支持不太好(MySQL 5.5版本开始默认引擎才是I ...

  8. 部署和调优 1.8 samba 部署和优化-2

    Samba 可以实现 Linux 和 Windows 机器相互共享文件,这对我们来说是非常实用的.下面做几个实践,来了解samba,注意:在实践之前,请先检测 Selinux 是否关闭,否则可能会实践 ...

  9. DAY11-MYSQL多表查询

    一 介绍 本节主题 多表连接查询 复合条件连接查询 子查询 准备表 #建表 create table department( id int, name ) ); create table employ ...

  10. .net 4 安装未成功,无意中的解决办法!

    公司 电脑是chost的系统,由于使用时间过长,重装纯净版系统的话,代价太大,故网上寻求各种解决办法! 安装.net 4 总是失败,查看百度,各种: WIN7系统哈哈跟我的问题一样,我的刚才解决了:1 ...