原文:http://www.jianshu.com/p/5254f1277dba

内存的栈区 : 由编译器自动分配释放, 存放函数的参数值, 局部变量的值等. 其操作方式类似于数据结构中的栈.

内存的堆区 : 一般由程序员分配释放, 若程序员不释放, 程序结束时可能由OS回收. 注意它与数据结构中的堆是两回事, 分配方式倒是类似于链表.


copy方法和mutableCopy方法

如果你想要创建一个对象, 该对象与源对象的内容一致, 那么你可以考虑用拷贝(copy或mutableCopy), 首先, 我将会利用字符串, 数组, 字典这三种常见的对象来说明copy与mutableCopy的区别.

NSString

NSString *string = @"Jerry";
[string copy] --> 拷贝出内容为Jerry的NSString类型的字符串
[string mutableCopy] --> 拷贝出内容为Jerry的NSMutableString类型的字符串

NSDictionary

NSDictionary *dict = @{@"name" : @"Jerry"};
[dict copy] --> 拷贝出内容与dict相同的NSDictionary类型的字典
[dict mutableCopy] --> 拷贝出内容与dict相同的NSMutableDictionary类型的字典

NSArray

NSArray *array = @[@"Jerry"];
[array copy] --> 拷贝出内容与array相同的NSArray类型的数组
[array mutableCopy] --> 拷贝出内容与array相同的NSMutableArray类型的数组

总结

  1. copy拷贝出来的对象类型总是不可变类型(例如, NSString, NSDictionary, NSArray等等)
  2. mutableCopy拷贝出来的对象类型总是可变类型(例如, NSMutableString, NSMutableDictionary, NSMutableArray等等)

深拷贝与浅拷贝

何为深拷贝, 何为浅拷贝?

深拷贝 : 拷贝出来的对象与源对象地址不一致! 这意味着我修改拷贝对象的值对源对象的值没有任何影响.
浅拷贝 : 拷贝出来的对象与源对象地址一致! 这意味着我修改拷贝对象的值会直接影响到源对象.

这里需要纠正网上一些错误的观点(以下为错误观点)

copy都是浅拷贝, mutableCopy都是深拷贝

我们知道, 当我们用copy从一个可变对象拷贝出一个不可变对象时, 这种情况就属于深拷贝而不是浅拷贝!!

注意 ! 深拷贝与浅拷贝也有相对之分!!!看下面

对于NSString对象, 确实深拷贝就是深拷贝, 浅拷贝就是浅拷贝, 没有任何异议.
但是对于NSArray, NSDictionary, NSSet这些容器类的对象呢? 当然浅拷贝依然是指针拷贝, 那深拷贝意味着连同容器及其容器内的对象一并拷贝吗? 还是只拷贝容器对象, 对容器内的对象则只是简单引用呢? 这里有两种情况, 我姑且把它称为不完全深拷贝完全深拷贝

不完全深拷贝

不完全深拷贝就是只拷贝容器对象(拷贝一个壳), 而对于容器内的对象则只保存一份引用.

不完全深拷贝.png

所以我们知道, 就算我们修改copyArray不会影响到源array, 但是我通过copyArray修改数组内的object, 对应地源array内的object也会随之修改, 大家可以自行测试.

完全深拷贝

完全深拷贝就是连同容器内的对象在内, 完完全全拷贝一份出来

完全深拷贝.png

通过图片可以很清楚地知道, 这种情况下无论是修改copyArray还是通过copyArray修改数组内的object, 对源array都不会造成半点影响.

ps : 默认状态下深拷贝指的是不完全深拷贝, 如要实现完全深拷贝, 则要重写copyWithZone: 方法, 自行实现完全深拷贝的需求. 大体思路如下, 在copyWithZone: 里对象赋值上不直接赋值而是通过copy方法即可实现, 这里不作具体讨论.既然有朋友问到, 那就贴上示例代码吧
// Person.m
- (id)copyWithZone:(NSZone *)zone
{
Person *cpyPerson = [[Person allocWithZone:zone] init];
cpyPerson.name = self.name;
cpyPerson.age = self.age;
return cpyPerson;
}
// NSArray
- (id)copy
{
NSArray *cpyArray = [[NSArray alloc] initWithArray:self copyItems:YES];
return cpyArray;
}
// main.m
Person *p1 = [[Person alloc] init];
Person *p2 = [[Person alloc] init];
NSArray *array = @[p1, p2];
NSArray *cpyArray = [array copy];
NSLog(@"%@ - %@", array, cpyArray);
// 输出结果
(
"<Person: 0x100204af0>",
"<Person: 0x100206b20>"
) - (
"<Person: 0x100207910>",
"<Person: 0x1002074d0>"
)

这样就能办到完全深拷贝的目的了.
ps : 官方文档说明copy方法内部默认会调用copyWithZone方法的, 但是NSArray因为未知的原因导致其copy方法不会调用copyWithZone (可能是因为OC中已经废弃了zone这个概念, 苹果官方文档有说), 所以这里我就利用分类重写了NSArray的copy方法, 实际上苹果并不推荐这么做.


block为什么要用copy?

首先, block是一个对象, 所以block理论上是可以retain/release的. 但是block在创建的时候它的内存是默认是分配在栈(stack)上, 而不是堆(heap)上的. 所以它的作用域仅限创建时候的当前上下文(函数, 方法...), 当你在该作用域外调用该block时, 程序就会崩溃.

Apple官方文档

意思就是 : 一般情况下你不需要自行调用copy或者retain一个block. 只有当你需要在block定义域以外的地方使用时才需要copy. Copy将block从内存栈区移到堆区.

其实block使用copy是MRC留下来的也算是一个传统吧, 在MRC下, 如上述, 在方法中的block创建在栈区, 使用copy就能把他放到堆区, 这样在作用域外调用该block程序就不会崩溃. 但在ARC下, 使用copy与strong其实都一样, 因为block的retain就是用copy来实现的, 所以block使用copy还能装装逼, 说明自己是从MRC下走过来的..嘿嘿


copy相对于直接赋值的好处

看看以下代码 :

直接赋值

大家猜猜控制台输出是啥? 是( Kobe ), ( Kobe, McGrady )吗?
错了错了!!!

array = (
Kobe,
McGragy
), mArray = (
Kobe,
McGragy
)

为什么??? 明明可变数组添加对象是在赋值之后, 为什么后面添加对象还会影响到不可变数组呢??
原因很简单, 因为Objective-C支持多态.
所以表面上self.array是NSArray对象, 其实骨子里是NSMutableArray对象.这样的话将会对后期DEBUG增加很大的成本, 可能会导致莫名其妙的错误.
再看以下代码 :

使用copy

大家再来猜一下输出会是什么?
没错!

array = (
Kobe
), mArray = (
Kobe,
McGragy
)

这样就能保证不管赋值的是可变还是不可变数组, NSArray就是NSArray了!(你爸就是你爸, 不可能变成你了)

所以大家现在知道为什么@property中的NSString, NSArray, NSDictionary属性为什么大多时候用copy而不用strong的原因了么?


总结

这里做出了一张图, 帮助新手弄清楚copy与mutableCopy的区别, 大神请无视^_^

copy与mutableCopy

如果能够在你的工程中正确使用copy, 将会对你的程序有不小的帮助.细节决定成败嘛!!

iOS中的copy的更多相关文章

  1. 小结 iOS 中的 copy

    预备知识 : 内存的栈区 : 由编译器自动分配释放, 存放函数的参数值, 局部变量的值等. 其 操作方式类似于数据结构中的栈. 内存的堆区 : 一般由程序员分配释放, 若程序员不释放, 程序结束时可能 ...

  2. iOS中assign、copy 、retain等关键字的含义

    iOS中assign.copy .retain等关键字的含义  转自:http://my.oschina.net/majiage/blog/267409 assign: 简单赋值,不更改索引计数cop ...

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

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

  4. iOS中的数据持久化方式

    iOS中的数据持久化方式,基本上有以下四种:属性列表.对象归档.SQLite3和Core Data. 1.属性列表 涉及到的主要类:NSUserDefaults,一般 [NSUserDefaults ...

  5. iOS:iOS中的多控制器管理

    iOS中的控制器有三种创建方式: 1.通过storyboard创建 UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@" ...

  6. IOS中的Block与C++11中的lambda

    ios中的block 可以说是一种函数指针,但更确切的讲,其实际上其应该算是object-c对C++11中lambda的支持或者说是一个语言上的变体,其实际内容是一样的,C++的lambda我已经有简 ...

  7. iOS中几种常用的数据存储方式

    自己稍微总结了一下下,方便大家查看 1.write直接写入文件的方法 永久保存在磁盘中,可以存储的对象有NSString.NSArray.NSDictionary.NSData.NSNumber,数据 ...

  8. iOS中的存储方式

    1.Plist 1.1 了解沙盒 每个iOS应用都有自己的应用沙盒(应用沙盒就是文件系统目录),与其它文件系统隔离.应用必须呆在自己的沙盒里.其它应用不能访问该沙盒. 一个程序中所有的非代码文件都可以 ...

  9. iOS中JSONModel的使用

    iOS中JSONModel的使用   流弊的JSON数据模型框架 https://github.com/jsonmodel/jsonmodel 版本 1.3.0 如果你喜欢JSONModel,并且使用 ...

随机推荐

  1. Tushare数据的绘图操作

    1.在代码里调试学习实在费劲,可以把数据取到df里,在交互界面里慢慢调试 2.柱状图 绘制柱状图,默认情况下乱,数据太密了 改用曲线图

  2. Spring 集成 redistemplate

    jar包 <version.clients.jedis>2.7.2</version.clients.jedis><version.data.redis>1.6.2 ...

  3. [Functional Programming] Introduction to State, thinking in State

    Recently, I am learning Working with ADT. Got some extra thought about State Monad. Basiclly how to ...

  4. Direct2D教程VII——变换几何(TransformedGeometry)对象

    目前博客园中成系列的Direct2D的教程有 1.万一的 Direct2D 系列,用的是Delphi 2009 2.zdd的 Direct2D 系列,用的是VS中的C++ 3.本文所在的 Direct ...

  5. matlab之simulink仿真入门

    Matlab Simulink仿真工具的应用 ****Simulink是一个用来对动态系统进行建模.仿真和分析的软件包.使用Simulink来建模.分析和仿真各种动态系统(包含连续系统.离散系统和混合 ...

  6. Android 之 沉浸式状态栏及顶部状态栏背景色设置

    现在很多应用都引用了沉浸式状态栏,如QQ,效果下图: 效果很酷炫,其实设置也很简单.但是,需要注意的是,这种效果只能在API19以及以上版本中才能够做到. 方法一: 首先,如果想让界面Activity ...

  7. 如何随机获取数据库不连续ID的数据?

    这个问题的来由是我朋友要为一网站实现一个标签云功能,和我交流后我给出了一个方案,在此略作记录,亦求拍砖. 大概需求这是样的: 在数据库有一张表A如下图: 其中id字段的值未必是连续的,现在我朋友要做的 ...

  8. Spark的运行模式(2)--Yarn-Cluster和Yarn-Client

    3. Yarn-Cluster Yarn是一种统一资源管理机制,可以在上面运行多种计算框架.Spark on Yarn模式分为两种:Yarn-Cluster和Yarn-Client,前者Driver运 ...

  9. PPT里面的背景音乐找不到?

      ppt,找不到播放器,却有音乐播放!如何实现? 原来是在幻灯片切换处的音效添加的音乐~   文章来源:刘俊涛的博客 欢迎关注,有问题一起学习欢迎留言.评论

  10. infobright系列三:数据导入乱码

    1:目前在用的是社区版的infobright,不支持DML功能,只能用LOAD DATA方式导入数据. 如果元数据中有特殊控制字符,导入过程中经常会报错 2: 设置Reject File导入之前,设定 ...