iOS copy和mutableCopy 整理
copy 和 mutableCopy 你真的理解吗?最近发现很多面试者基本都不能很好地回答这个问题。所以整理一下。
copy和mutableCopy的概念:
copy
浅拷贝,不拷贝对象本身,仅仅是拷贝指向对象的指针。
NSString *str1 = @"str1";
NSString *str2 = [str1 copy];
NSLog(@"\nstr1 = %@ str1P = %p \n str2 = %@ str2P = %p", str1, str1, str2, str2);
/*输出结果,可以得出下图结论
str1 = str1 str1P = 0x104d75180
str2 = str1 str2P = 0x104d75180
*/
mutableCopy
深拷贝,是直接拷贝整个对象内存到另一块内存中。
NSMutableString *mStr1 = [@"123" mutableCopy];
NSMutableString *mStr2 = [mStr1 mutableCopy];
NSLog(@"\n mStr1 = %@ mStr1P = %p \n mStr2 = %@ mStr2P = %p", mStr1, mStr1, mStr2, mStr2);
/*输出结果,可以得出下图结论
mStr1 = 123 mStr1P = 0x6000004460c0
mStr2 = 123 mStr2P = 0x600000446420
*/
大部分人可能理解到这里就结束了。
当继续往下提问,如果是浅拷贝,改变str1的值,str2的值会变化吗?
问题1:上面浅拷贝的情况下,改变str1的值,str2的值会变化吗?
大部分人会不假思索的回答,因为str1 和 str2 指向同一个内存空间,str1变化,str2的值也会变化。
分析的很有道理,老铁看似没毛病毛病!
但是copy还有它的特点:
- 修改源对象的属性和行为,不会影响副本对象
- 修改副本对象的属性和行为,不会影响源对象
我们来试验一下:
NSString *str1 = @"str1";
NSString *str2 = [str1 copy];
str1 = @"asdf";
NSLog(@"\nstr1 = %@ str1P = %p \n str2 = %@ str2P = %p", str1, str1, str2, str2);
/*输出结果,修改str2 同理
str1 = asdf str1P = 0x10776b1a0
str2 = str1 str2P = 0x10776b180
*/
那为什么NSString *str2 = [str1 copy];
是不同的指针指向同一块内存空间,str1 从新赋值 后两个内存空间就不一样了呢?
因为str2 = str1
的时候,两个字符串都是不可变的,指向的同一块内存空间中的 @"str1"
,是不可能变成@"abcd"
的。所以这个时候,为了优化性能,系统没必要另外提供内存,只生成另外一个指针,指向同一块内存空间就行。
但是当你从新给 str1
或者str2
赋值的时候,因为之前的内容不可变,还有互不影响的原则下,这个时候,系统会从新开辟一块内存空间。
问题2:copy 一个可变的数组,会出现什么结果?
我们直接上结果吧。。。
NSMutableArray *mArr1 = [@[@"123", @"456", @"asd"] mutableCopy];
NSMutableArray *mArr2 = [mArr1 copy];
NSLog(@"\n mArr1 = %@ mArr1P = %p mArr1 class = %@ \n\n mArr2 = %@ mArr2P = %p mArr2 class = %@", mArr1, mArr1, [mArr1 class], mArr2, mArr2, [mArr2 class]);
/*输出结果
mArr1 = (
123,
456,
asd
)
mArr1P = 0x60400025db20
mArr1 class = __NSArrayM
mArr2 = (
123,
456,
asd
)
mArr2P = 0x60400025dd30
mArr2 class = __NSArrayI
*/
从结果看出,内存地址不一样,而且mArr2
是不可变的。copy
为什么不是指针指向了?
首先,mArr2
是通过copy
得来的,关键点在于copy
,和mArr1
无关,所以他是不可变的。
另外,mArr1
指向的内存空间是可变的,如果对mArr1
进行修改,同一内存空间的内容就会变化。 遵循相会不影响的原则,加上mArr2
是不可变的,mArr1
的内存空间已经不合适,所以此时的 copy
从新开辟内存空间。
问题3:用 copy 修饰 NSMutableArray @property (nonatomic, copy) NSMutableArray *mArr;
,对mArr 赋值会有什么结果?
NSArray *arr = @[@"123", @"456", @"asd"];
self.mArr = [arr mutableCopy];
NSLog(@"\n arrP = %p \n self.mArrP = %p, self.mArr class = %@", arr, self.mArr, [self.mArr class]);
/*输出结果
arrP = 0x60000044d1a0
self.mArrP = 0x60000044d1a0, self.mArr class = __NSArrayI
*/
可以看出内存地址不一样,但是_mArr
是不可变的数组。
因为 _mArr
声明的时候是用 copy
修饰,那么就限制了他为不可变的数组。 赋值的时候是用mutableCopy
,可变数组的复制方法,所以会从新分配内存。
NSArray *arr = @[@"123", @"456", @"asd"];
self.mArr = arr;
NSLog(@"\n arrP = %p \n self.mArrP = %p, self.mArr class = %@", arr, self.mArr, [self.mArr class]);
/*输出结果
arrP = 0x60400044ecd0
self.mArrP = 0x60400044ecd0, self.mArr class = __NSArrayI
*/
直接赋值,内存地址没变。
问题4:浅拷贝,不拷贝对象本身,仅仅是拷贝指向对象的指针。深拷贝,是直接拷贝整个对象内存到另一块内存中。 有什么看法?
浅拷贝,不拷贝对象本身,仅仅是拷贝指向对象的指针。不够严谨,在一些特殊请款下,还是会拷贝整个对象内存到另一块内存中。
总结:
- 用copy修饰的 或者赋值的 变量肯定是不可变的。
- 用copy赋值,要看源对象是否是可变的,来决定只拷贝指针,还是也拷贝对象到另一块内存空间
- 对象之间mutableCopy赋值,肯定会拷贝整个对象内存到另一块内存中
- 对象之间赋值之后,再改变,遵循互不影响的原则
原文地址:https://www.cnblogs.com/jiuyi/p/11451226.html
- 尝试使用Memcached遇到的狗血问题
- Enumerable#Zip 实现一下
- 更新自己,不要影响其他人
- 【译】Spring官方教程:Spring Boot整合消息中间件RabbitMQ
- [实录]解决Migrator.Net 小bug
- Jenkins Pipeline插件十大最佳实践!
- Spring Cloud Hystrix的请求合并
- JQuery JCshare 0.1 分享插件
- Java中的即时编译(Just-in-time compilation)
- 无尽的忙碌换来幸福的日子
- 消费者驱动的微服务契约测试套件:Spring Cloud Contract
- 自己做的一个小程序 可采集、导出、模板、配置
- 分布式消息队列 RocketMQ 源码分析 —— Message 拉取与消费(上)
- .NET反射、委托技术与设计模式
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 你对CSS权重真的足够了解吗?
- python opencv实现图片缺陷检测(讲解直方图以及相关系数对比法)
- Js 的事件循环(Event Loop)机制以及实例讲解
- 你不知道的js中关于this绑定机制的解析[看完还不懂算我输]
- 解决Django部署设置Debug=False时xadmin后台管理系统样式丢失
- 算法-查找斐波纳契数列中第 N 个数
- 在python中修改.properties文件的操作
- Django Xadmin多对多字段过滤实例
- 算法-姓名去重
- Ubuntu18.04安装 PyCharm并使用 Anaconda 管理的Python环境
- Python Opencv中用compareHist函数进行直方图比较对比图片
- 面试题-树状数组结构转化
- Xadmin+rules实现多选行权限方式(级联效果)
- Android仿主流壁纸App设置界面
- Android中WindowManager与WMS的解析