首先先引用阳神Sunny博客中的一道面试题:

用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

这说明对于我们来讲,弄懂copy还是十分有必要的,下面就让我们来一起看看copy的黑魔法。


copy是什么,有什么用?

1.是什么?

首先copy和mutableCopy是方法,是NSObject内定义的方法。还有对应的类方法copyWithZone:(struct _NSZone *)zone以及两个协议NSCopying和NSMutableCopying

  • 其中,+copy:、+copyWithZone:简单地说,是为了让”类”对象也符合NSCopying协议,也可以作为key插入NSDictionary中。又因为类对象全局只能存在一份,所以+copy:、+copyWithZone:方法只是简单返回self,而且这两个方法在ARC环境下也是不可用的。

  • 对于-copy、-mutableCopy,这两个方法被调用就会产生一个新的副本对象,里面会直接把-copyWithZone:、-mutableCopyWithZone:的值返回。但是NSObject并没有实现-copyWithZone:和-mutableCopyWithZone:,所以子类对象要使用-copy、-mutableCopy就必须去实现NSCopying和NSMutableCopying协议。不过常见的NSString、NSArray、NSDictionary等都已遵守了上面两个协议。

  • 最后NSZone已经被Apple抛弃,可不去追究。

2.有什么用?

copy顾名思义就是拷贝或者说克隆,所以copy的目的就是复制一份原来的内容,进一步思考为什么需要拷贝?显然:拷贝的目的就是改变原来的内容不影响副本,改变副本也不影响原来的内容

深拷贝、浅拷贝

下面通过NSString、NSMutableString和NSArray、NSMutableArray举例说明下上述两种拷贝是什么意思。

1.NSString、NSMutableString非容器对象分析

  • 第一种情况:

我们注意到str和通过[str mutableCopy]的copyStr两者内容一致,但内容地址不同,也就是重新创建了一个对象。为什么要新建一个对象?
1>:拷贝的目的是互不干扰,所以需要生成一个新的对象。
2>:str是一个不可变的对象, 而通过mutableCopy拷贝出来的对象必须是一个可变的对象, 所以生成一个新的对象

  • 第二种情况:

我们发现copyStr在通过[str mutableCopy]之后,并没有因为str的改变而改变,符合拷贝的目的;同时这种情况拷贝也生成了一个新的对象,原因同上。

  • 第三种情况:

此时copyStr在通过[str copy]之后,也没有因为str的改变而改变,同样也生成了一个新的对象,为什么?
1>:拷贝的目的是互不干扰,所以需要生成一个新的对象。因为str是可变的,为了防止str改变后影响copyStr的值,所以必须新建对象。

  • 第四种情况:

此时我们发现并没有新建对象,这又是为什么呢?
1>通过不可变对象调用了copy方法, 那么不会生成一个新的对象
2> 因为原来的对象是不能修改的, 拷贝出来的对象也是不能修改的,既然两个都不能修改, 所以永远不能影响到另外一个对象,已经符合拷贝的目的 。所以,OC为了对内存进行优化, 就不会生成一个新的对象

2.NSArray、NSMutableArray容器对象分析

首先容器对象和非容器对象一样同样遵从下面的总结:

如果对一不可变对象复制,copy是指针复制(浅拷贝)、mutableCopy就是对象复制(深拷贝)。
如果是对可变对象复制,都是深拷贝,但是copy返回的对象是不可变的。


但是对于容器对象有两点特殊的地方:

  • (1).NSMutableArray和NSArray多次copy有差别,请看图:

也就是说:NSMutableArray多次copy每次都会新建对象而NSArray多次copy只新建一次对象。

  • (2)对于容器而言,其元素对象始终是指针复制。这样我们就可以修改一个容器的值从而影响到其他拷贝的容器。


如何实现元素对象也是对象复制?可以用归档的方法实现了真正的元素对象拷贝。

3.总结

正是因为调用copy方法有时候会生成一个新的对象, 有时候不会生成一个新的对象所以:

如果没有生成新的对象, 我们称之为浅拷贝, 本质就是指针拷贝
如果生成了新的对象, 我们称之为深拷贝, 本质就是会创建一个新的对象
最后:最重要的还是记住拷贝的目的,这样理解深浅拷贝都会变得非常简单,改变原来的内容不影响副本,改变副本也不影响原来的内容

 

copy内存管理

MRC下
如果是浅拷贝:不会生成新的对象,但是系统就会对原来的对象进行retain。
如果是深拷贝:会生成新的对象,系统不会对原来的对象进行retain。

copy和property

  • @property (nonatomic, copy) NSString name;

    这里为什么用copy,原因依然是拷贝的目的*改变原来的内容不影响副本,改变副本也不影响原来的内容,因为如果不使用copy,别人修改了外界的name属性也会受影响,也就没有达到拷贝的目的。

  • @property (nonatomic, copy) myBlock pBlock;block作为属性的时候也用copy,原因是:

    首先这涉及到MRC时代。因为MRC时期,为了防止block内用到的变量提前释放导致程序崩溃,使用copy将block存放到堆中,此时block会对内部变量进行一次retain操作,从而防止意外清空。同时block放入堆中也会带来一个新的问题,self持有block的引用,如果在block中使用self就会产生循环引用,所以不论MRC还是ARC,我们都分别用blcok和weak来修饰self。

    下面分别比较下MRC中retain和ARC中strong和copy的区别,看看strong是否也能达到同样的效果。


  • 1.MRC下用retain修饰block.

我们清楚的看到当点击屏幕调用blcok时,block内部用到的对象的提前释放,导致程序崩溃,而设置property属性的时候,Xcode也给了提示用copy替换retain。

  • 2.ARC下用strong修饰block.

此时我们发现ARC下strong得效果和copy是一样的,同样可以防止内容对象被提前释放。


所以结论copy是MRC下的产物,今天ARC时代为什么block依然使用copy,我想更多的是一种习惯问题

认识copy关键的更多相关文章

  1. iOS经典面试题总结--内存管理

    iOS经典面试题总结--内存管理 内存管理 1.什么是ARC? ARC是automatic reference counting自动引用计数,在程序编译时自动加入retain/release.在对象被 ...

  2. iOS之内存管理浅谈

    1.何为ARC ARC是automatic reference counting自动引用计数,在程序编译时自动加入retain/release.在对象被创建时retain count+1,在对象被re ...

  3. 开发时复制aspx网页的方法

    简单的copy /paste    *.aspx网页,所使用的是同一个CodeBehind  ,这往往不是我们所想要的!!!我们一般都希望使这两个网页具有各自的  *.cs文件.步骤:①新建一个Web ...

  4. TPM:dTPM(硬件)和fTPM(固件模拟的软件模块)

    转:Bitlocker.TPM和系统安全 自从微软在Windows Vista首次引入Bitlocker以来,它已经越来越多的出现在我们的周围.尤其是企业用户,Bitlocker的保护已经变得不可缺少 ...

  5. ios 进阶技术点

    1.Runtime的消息转发机制 消息转发机制基本上分为三个步骤: 1. 动态方法解析 2. 备用接收者 3. 完整转发 2.Runloop的工作原理 runloop.autorelease pool ...

  6. 为IHttpClientFactory添加动态命名配置

    某些时候我们需要为HttpClient动态配置一些东西, 例如证书等, 参考博问 如何使用IHttpClientFactory动态添加cer证书. 例如服务是一个回调服务, 而被回调方采用了自定义的h ...

  7. HEC-ResSim原文档

              HEC-ResSim Reservoir System Simulation             User's Manual       Version 3.1 May 201 ...

  8. Qt之图标切分与合并(关键是使用QPixmap的copy函数来拷贝整张图片的某个区域)

    有些时候会将多张有相同功能的图片绘制成一张,不管是使用或者绘制上都会方便很多.对美工与开发者来说也都是一件省事.省力.更省心的方式.二全其美,又何乐而不为呢... 例如:QQ等级           ...

  9. 小学徒成长系列—StringBuilder & StringBuffer关键源码解析

    在前面的博文<小学徒成长系列—String关键源码解析>和<小学徒进阶系列—JVM对String的处理>中,我们讲到了关于String的常用方法以及JVM对字符串常量Strin ...

随机推荐

  1. ruby 方法重载

    class MyClass def sayHello return "hello from MyClass" end def sayGoodbye return "Goo ...

  2. Java:JXL解析Excel文件

    项目中,有需求要使用JXL解析Excel文件. 解析Excel文件 我们先要将文件转化为数据流inputStream. 当inputStream很大的时候 会造成Java虚拟器内存不够 抛出内存溢出 ...

  3. 【HDOJ】1258 Sum It Up

    典型的深搜,剪枝的时候需要跳过曾经搜索过的相同的数目,既满足nums[i]=nums[i-1]&&visit[i-1]==0,visit[i-1]==0可以说明该点已经测试过. #in ...

  4. PHPCMS 2008 SQL注入漏洞

    漏洞版本: PHPCMS 2008 漏洞描述: escape()方法无法过滤$genre变量进行二次URL编码,导致SQL注入漏洞产生. <* 参考 http://www.yunsec.net/ ...

  5. 按CTRL,SHIFT,ALT等键扩展easyui的datagrid多选实现

    //------------------------------------------------------------------------------- // 当然页面文件中还需要引入的文件 ...

  6. C#图片处理之: 另存为压缩质量可自己控制的JPEG

    处理图片时常用的过程是:读入图片文件并转化为Bitmap -> 处理此Bitmap的每个点以得到需要的效果 -> 保存新的Bitmap到文件使用C#很方便的就可以把多种格式的图片文件读到B ...

  7. MVC 3.0 在各个版本IIS中的部署

    概述: 最近在做一个MVC 3的项目,在部署服务器时破费了一番功夫,特将过程整理下来,希望可以帮到大家! 本文主要介绍在IIS5.1.IIS6.0.IIS7.5中安装配置MVC 3的具体办法! 正文: ...

  8. 谈谈分布式事务之二:基于DTC的分布式事务管理模型[下篇]

    [续上篇] 当基于LTM或者KTM的事务提升到基于DTC的分布式事务后,DTC成为了本机所有事务型资源管理器的管理者:此外,当一个事务型操作超出了本机的范 围,出现了跨机器的调用后,本机的DTC需要于 ...

  9. 谈谈分布式事务之一:SOA需要怎样的事务控制方式

    在一个基于SOA架构的分布式系统体系中,服务(Service)成为了基本的功能提供单元,无论与业务流程无关的基础功能,还是具体的业务逻辑, 均实现在相应的服务之中.服务对外提供统一的接口,服务之间采用 ...

  10. c#打印机设置,取得打印机列表及相应打印机的所有纸张格式

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...