Objective-C对象的浅拷贝与深拷贝探究
05 December 2020
Objective-C对象的浅拷贝与深拷贝探究
iOS提供了2个拷贝方法
- copy 不可变拷贝,产生不可变副本;
- mutableCopy可变拷贝,产生可变副本;
深拷贝和浅拷贝
深拷贝
内容拷贝,产生新的对象; 浅拷贝
指针拷贝,没有产生新的对象;
copy和mutableCopy 图解
copy | mutableCopy | |
---|---|---|
NSString | NSString 浅拷贝 | NSMutableString深拷贝 |
NSMutableString | NSString深拷贝 | NSMutableString深拷贝 |
NSArray | NSArray浅拷贝 | NSMutableArray深拷贝 |
NSMutableArray | NSArray深拷贝 | NSMutableArray深拷贝 |
NSDictionary | NSDictionary浅拷贝 | NSMutableDictionary深拷贝 |
NSMutableDictionary | NSDictionary深拷贝 | NSMutableDictionary深拷贝 |
- 由此可知,包含’mutable’字串符号的拷贝都是深拷贝,不包含的是浅拷贝。 浅拷贝中的
copy
方法等价于retain
;
@property 属性
@property
中的copy
关键字作用是:调用被赋值给属性的对象的copyWithZone
方法,并将返回值赋值给属性;- @property只是告诉编译器,帮我生成 setter和 getter方法,也就是声明并实现属性变量的setter和 getter方法;
copy 与 mutablecopy 对比
- 如果是 copy 方法,则调用对象的 copyWithZone方法;
- 如果是mutablecopy,则调用对象的mutableCopyWithZone 方法;
- 如果 copy = 0,mutablecopy = 0,那么最终会调用objc_retain方法;
copy
是为了获得不可变副本,mutableCopy
是为了获取可变副本,而修饰属性的关键字只有copy
;- 在属性中不要使用
copy
修饰NSMutableArray
、NSMutableDictionary
等可变类型;
生成 cpp 文件可以使用如下的编译指令:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc a.m -o a.cpp
用 strong 来修饰字符串属性会出现的问题
如果使用strong来修饰NSString
属性,给这个属性赋值一个NSMutableString
对象,然后通过对这个属性加上强制类型转换成NSMutableString
类型,就可以实现直接修改这个属性内存地址中的值了。
Block的拷贝
ARC 中的 Block 属性使用 copy 和 strong 修饰 Block 都可以
- 使用 copy 修饰时,相当于主动调用 copy 方法,StackBlock 会被拷贝到堆上而成为 MallocBlock;
- 使用 strong 修饰时,ARC 模式下,会自动调用 copy 方法,StackBlock 会被拷贝到堆上而成为 MallocBlock;
MRC 中的 Block 属性必须使用 copy 修饰
- 使用 copy 修饰时,相当于主动调用 copy 方法,StackBlock 会被拷贝到堆上而成为 MallocBlock;
- MRC 模式下,strong 修饰 Block 只会 retain,不会自动调用 Block 的 copy 方法;
关于copy的总结:
- copy 的目的是创建一个互不干扰,相互独立的副本;
- copy 无论是直接调用还是修饰属性,其本质是调用copyWithZone和mutableCopyWithZone方法;
- 深浅复制的区别在于返回值是否为新创建的对象,和调用 copy 的哪个方法无关;
- 使用 copy 修饰属性的关键目的是告诉使用者,这个不要直接修改属性所指向内存中的值;
- 修饰可变类型的对象,比如可变数组,严禁使用 copy 修饰;
- copy 的本质是调用 copy 协议中的两个方法,只是系统对字符串、数组、字典、NSNumber 和 Block 实现了该协议的两个方法,其中两个方法所实现的逻辑大同小异;
- copy 修饰属性的本质是自动调用新值的 copy 方法以获取一个不可变对象,属性无 mutableCopy 修饰,因为没有必要;
- copy 修饰 Block 属性的本质仍然是调用 copy 方法,只是其内部实现是将存放在栈上的 block 转移到堆上,否则栈中的 Block 被销毁后会访问指向该 Block 的指针会产生坏内存访问问题;