iOS Copy与MutableCopy 和 Copy与Strong 深度解析

开发中,数据处理是整个项目的重中之重,清晰的数据结构,安全高效的处理流程,能大大提高开发效率和系统的稳定性。数据是事物状态和变化的记录,具有可修改性和拷贝性,当多处使用,并有可能改变时,为了保障原数据的不变,我们需要拷贝一份新的数据,改变新的数据,而不改变原数据。数据处理中的,操作权限控制,数据的传递,数据的深、浅拷贝等。今天主要深度分析下,Copy与MutableCopy 和 Copy与Strong 区别及使用。

1 深浅拷贝的区别(Copy与MutableCopy)

浅拷贝:指针拷贝,不产生新的对象,源对象的引用计数器+1;

深拷贝:对象拷贝,会产生新的对象,源对象的引用计数器不变;

判断是浅拷贝和深拷贝就看一下两个变量的内存地址是否一样,一样就是浅拷贝,不一样就是深拷贝,也可以改变一个变量的其中一个属性值看两者的值都会发生变化;

1.1 系统原生的对象深浅拷贝区别

NSObject类提供了copy和mutableCopy方法,通过这两个方法即可拷贝已有对象的副本,主要的系统原生对象有:NSString和NSMutableString、NSArray和NSMutableArray、NSDictionary和NSMutableDictionary、NSSet和NSMutableSet。 NSValue和NSNumber 只遵守的NSCopying协议。

注意:基本数据类型(assign修饰),没有对应的指针,是直接赋值操作,没有,也无需copy 操作。

这里以NSString 和 NSMutableString为例演示说明。

NSString —— copy/mutableCopy

打印结果:

小结论:在字符串是直接赋值的,是否生成新对象是和 = 右边有关的,如果 = 右边是mutableCopy才会生成新对象。

NSMutableString —— copy/mutableCopy

打印结果:

小结论:只要=右边从创建到赋值,至少包含一个NSMutable便会重新生成一个对象。如果对一个不可变对象拷贝,copy是指针拷贝(浅拷贝)和mutableCopy就是对象拷贝(深拷贝)。但是,无论原对象是否含有NSMutable,copy返回的对象都是不可变的。

注意:其他对象NSArray、NSMutableArray 、NSDictionary、NSMutableDictionary、NSSet、NSMutableSet一样适用。

image

1.2 自定对象的深浅拷贝

实际开发中,所使用的数据模型基本都是自定义对象,并常见自定义对象的嵌套使用,我们如何实现自定义对象的深度拷贝呢?

1.2.1 前期准备

创建两个对象 PersonItem、DogItem,.m文件中暂时不做处理,.h文件参考如下:

DogItem.h

PersonItem.h

参考本文1.1章节中的使用

来Run 下,竟然崩溃了。。。未找到PersonItem类的 对象方法copyWithZone:

我们需要实现

注意:细心的你是否发现,我明明调用的是copy 方法 ,却要实现copyWithZone:而不copy?

我们需要通过copyWithZone: 或 mutableCopyWithZone: 来开辟存储空间,拷贝对象及其实例变量。无需在.h文件中声明这两个方法,这是因为系统方法的 copy(mutableCopy)中实现了对 copyWithZone:(mutableCopyWithZone: )的调用。

为了编码规范化,你需要自定义对象遵守 NSCopying 或NSMutableCopying 协议,其实此处不遵守该协议,只要正确实现了上述的两个方法,不会报错能也正常使用,请养成规范编码的好习惯。

1.2.2 关键步骤: 实现copyWithZone: 方法

在自定义对象的.m文件中实现copyWithZone: 方法,主要实现形式有一下四种。

方法一:伪拷贝不可用哦

打印结果:

小结论:伪拷贝,指针拷贝,没有开辟内存空间,原对象引用计数器加1,新对象及其实例变量的指针地址都与原对象一样。

方法二:浅拷贝(单层拷贝)

打印结果:

小结论:浅拷贝,开辟了内存空间,原对象引用计数器不变,对象的指针地址与原对象不同,但新对象的实例变量初始化的指针地址与原对象的实例变量指针地址一样,当新对象的该实例变量再次赋值后,该实例变量的指针地址变更。对象嵌套时,不能拷贝dog对象的实例变量的内容。

方法三:深拷贝(完全拷贝)

打印结果:

小结论:深拷贝,开辟了内存空间,新对象及其实例变量的指针地址都与原对象都不一样,如果涉及到容器中包含容器,深拷贝就是容器中每一层对象都是深拷贝。

方法四:深拷贝(完全拷贝)

打印结果:

小结论:深拷贝,开辟了内存空间, 新对象及其实例变量的指针地址都与原对象都不一样,如果涉及到容器中包含容器,深拷贝就是容器中每一层对象都是深拷贝。

综述:

方法一是伪拷贝只是指针拷贝,达不到拷贝对象内容的要求。

方法二是浅拷贝(单层拷贝),对象嵌套时,不能拷贝其所包含自定义对象的其实例,不能完全满足要求。

方法三是深拷贝(完全拷贝),只要所包含自定义对象实现了copy协议和相关方法实现,能完全拷贝对象及所包含自定义对象的内容。

方法四是深拷贝(完全拷贝),和方法三效果相同。方法四可以有另外的实现方式,不用遵守协议和实现copyWithZone: 方法,可以在自定义类中实现一个myCopy的对象方法,实现和方法四中一样,声明并显式的调用。

1.2.3 mutableCopyWithZone: 方法

mutableCopyWithZone和copyWithZone: 一样是个待实现的方法,关键的区别在于内部实现的区别。通常,copyWithZone: 做自定义对象的单层拷贝处理(有容器嵌套的化,只copy最外一层)。mutableCopyWithZone: 做自定义对象的完全拷贝处理(有容器嵌套的化,容器中每一层对象都做拷贝处理)。

2 Copy与Strong的区别

说完深浅拷贝,现在让我们一起梳理下property里的copy、strong的区别。

以NSString为例说明下,首先定义以下属性。

2.1 当外部赋给对应属性一个不可变(非mutable)的字符串 NSString

打印结果:

2.2 当外部赋给对应属性一个可变的字符串 NSMutableString

打印结果:

注意:此处的 string 是局部变量,存储在栈区,_strongString、_strongMutableString、_copyedString、_copyedMutableString 是全局变量,存储在堆区。他们有不同存储的空间,对应的指针地址会不同。

详情参考:iOS开发程序中各种变量的存储位置和程序返回变量的问题

__NSCFString 、__NSCFConstantString 都是NSSting抽象类的具体数据处理子类。NSTaggedPointerString

扩展阅读:

1、NSString NSCFString NSCFConstantString isMemberOfClass 遇到的相关的问题

2、iOS里的TaggedPointer[NSString篇]

这里的底层知识,我也不甚清楚,暂时只能说到这个程度,后续补充。

2.3 原理分析

让我们回顾下,我们定义的四个属性:

它们的set 方法是怎实现的呢?

property strong strongString 实际上就是对 strongString 做了

property copy copyString 实际上就是对 copyString 做了

property strong strongMutableString 实际上就是对 strongMutableString 做了

然而,property copy copyMutableString 实际上就是对 copyMutableString 做了

问题来了。。。。。。。

iOS 类型声明的 陷阱

看似你对 copyMutableString 属性 声明了 NSMutableString 类型,在使用上,调用了 NSMutableString 的方法,你会发现,程序运行到这行,直接崩溃了_,原因:无论原对象是否含有NSMutable,copy返回的对象都是不可变的。

NSString 实例,调用 NSMutableString 的方法,找不到该方法,当然就崩溃了。

因此,在声明名属性时,就会有一些不成文的规范。以字符串为例,为了保障数据的安全,以免被随意修改,尽量使用NSString(非mutable)类型,若不希望该属性内容的随着外部变化而影响初始值,应该用copy修饰,甚至用readonly 加强修饰。若希望该属性的内容随时变化并存储,可以用strong修饰,NSMutable* 类,都是继承自NS类,因此,NS 可以接收NSMuatble* 或NS * 类型的值,调用NS* 相关属性或方法,是正常的操作。或声明成NSMutableString类型,切记一定要用strong修饰。

推荐的属性声明方式:

根据需要选择,NSArray、NSMutableArray; NSDictionary、NSMutableDictionary; NSSet、NSMutableSet;类似。

不足之处,欢迎交流!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容