请稍侯

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 修饰NSMutableArrayNSMutableDictionary等可变类型;

生成 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 都可以

  1. 使用 copy 修饰时,相当于主动调用 copy 方法,StackBlock 会被拷贝到堆上而成为 MallocBlock;
  2. 使用 strong 修饰时,ARC 模式下,会自动调用 copy 方法,StackBlock 会被拷贝到堆上而成为 MallocBlock;

MRC 中的 Block 属性必须使用 copy 修饰

  1. 使用 copy 修饰时,相当于主动调用 copy 方法,StackBlock 会被拷贝到堆上而成为 MallocBlock;
  2. 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 的指针会产生坏内存访问问题;