当前位置: 首页 > news >正文

Copy修饰的NSArray

深复制与浅复制

简单点理解,深复制,在内存中拷贝一份新的对象。 浅复制,没有拷贝新的对象,只是一个地址的引用。 在赋值过程中深复制操作,原对象的引用计数不会增加,浅复制引用计数会加一。

copy操作和mutableCopy操作

一个类遵循NSCopying,NSMutableCopying协议并且实现相对应的初始化方法后,这个类就具有对象拷贝的能力。如果你自定义类没有遵守协议直接调用copy/mutableCopy程序会奔溃。拷贝协议的具体使用我这里不做扩展感兴趣的可以自行Google一下。有一些刚接触iOS开发的同学认为copy操作就是浅复制,mutableCopy就是深复制,这是一个非常错误的理解。以下是我的一些总结:

  • 可变的集合对象 + copy 得到一个新的对象(新对象不可变)深复制
  • 可变的集合对象 + mutablecopy 得到一个新的对象(新对象可变)深复制
  • 不可变集合对象 + copy 没有得到新的对象(地址的引用)浅复制
  • 不可变集合对象 + mutablecopy 得到一个新的对象(新对象可变)深复制 OC中集合对象NSArray、NSMutableArray、NSDictionary、NSMutableDictionary、NSSet、NSMutableSet等,另外NSString、NSMutableString也准守以上原则。实践出真理我们来看看代码中的效果:
    NSString *str = @"abc";
    NSMutableString *mStr = [NSMutableString stringWithString:str];
    NSString *strCopy = [str copy];
    NSString *strMutableCopy = [str mutableCopy];
    NSString *mStrCopy = [mStr copy];
    NSString *mStrMutableCopy = [mStr mutableCopy];
    
    NSArray *arr = @[@"abc"];
    NSMutableArray *mArr = [NSMutableArray arrayWithArray:arr];
    NSArray *arrCopy = [arr copy];
    NSArray *arrMutableCopy = [arr mutableCopy];
    NSArray *mArrCopy = [mArr copy];
    NSArray *mArrMutableCopy = [mArr mutableCopy];
    
    NSDictionary *dic = @{@"1":@"abc"};
    NSMutableDictionary *mDic = [NSMutableDictionary dictionaryWithDictionary:dic];
    NSDictionary *dicCopy = [dic copy];
    NSDictionary *dicMutableCopy = [dic mutableCopy];
    NSDictionary *mDicCopy = [mDic copy];
    NSDictionary *mDicMutableCopy = [mDic mutableCopy];

    NSSet *set = [NSSet setWithObject:@"1"];
    NSMutableSet *mSet = [NSMutableSet setWithSet:set];
    NSDictionary *setCopy = [set copy];
    NSDictionary *setMutableCopy = [set mutableCopy];
    NSDictionary *mSetCopy = [mDic copy];
    NSDictionary *mSetMutableCopy = [mDic mutableCopy];
复制代码

我们选取NSArray、NSMutableArray来看一下copy和mutableCopy的结果

stong与copy修饰符

strong和copy都可以用来修饰对象类型属性,strong修饰的属性,在将一个对象赋值给这个属性的时候,对象不会进行内存的拷贝,直接进行地址的引用,对象的引用计数+1;copy修饰的属性,如果该属性是一个非集合类型,那么赋值操作的效果和strong是一样的,如果该属性是一个集合类型,我们在下面在详细的解读。

NSMutableArray属性请使用strong修饰符

数学里面一个证明方式叫做反证法,接下来我们自定义一个类,增加一个NSMutableArray属性,使用copy来修饰

#import <Foundation/Foundation.h>
@interface TestObject : NSObject
@property (nonatomic, copy) NSMutableArray *mutableArr;
@end

#import "TestObject.h"
@implementation TestObject
- (instancetype)init{
    self = [super init];
// self.mutableArr = [NSMutableArray arrayWithObject:@"1"];
    _mutableArr = [NSMutableArray arrayWithObject:@"1"];
    [self.mutableArr addObject:@"2"];//这一步,如果使用点语法进行属性赋值,程序crash;如果直接使用成员变量方式赋值,代码没有问题
    return self;
}

//一般情况编译器会自动给属性添加get/set方法
- (void)setMutableArr:(NSMutableArray *)mutableArr{
    _mutableArr = [mutableArr copy];
}
@end
复制代码

执行下面代码

TestObject *testObject = [TestObject new];
testObject.mutableArr = [NSMutableArray arrayWithObject:@"1"];
[testObject.mutableArr addObject:@"2"];
复制代码

程序crash,控制台提示错误 [__NSSingleObjectArrayI addObject:]: unrecognized selector sent to instance 0x1c0017730 这一步我们就证明了如果我们使用copy来修饰一个可变的数组属性,然后通过点语法的方式把一个可变的数组赋值给该属性,最后该属性指向的是一个拷贝得到的全新的不可变数组,这个时候我们可以在代码中调用arrayWithObject等改变数组的api(因为@property (nonatomic, copy) NSMutableArray *mutableArr 告诉编译器这是一个可变的数组),但是在运行时,这个属性指向的是一个不可变的数组,引发unrecognized selector sent to instance 错误,从而导致crash。

NSArray属性请使用copy修饰符

我们定义一个NSArray类型的属性,目的是为了在初始化或者赋值操作后,这个数组集合就不能够改变的(增加或者删除元素等)。如果我们使用strong去修饰NSArray属性,并且我们把一个可变的数组NSMutableArray对象赋值给了该属性(父类的指针可以指向子类对象),strong并不会在内存中拷贝新的对象,只是一个地址的引用,所以NSArray类型的属性实质上指向的是一个NSMutableArray,这个时候如果其他地方还有一个NSMutableArray指针变量指向这个NSMutableArray对象,并且通过这个变量改变了数组,也会造成NSArray类型属性的改变,因为它们指向是一个地址内存,也就是一个对象。这就违背了我们设计的初衷,同时可能引发一些业务逻辑问题。

尾语

以上内容有任何的疑问、错误,请各路大神指出,同时希望能够帮助到大家

相关文章:

  • hive:导出数据记录中null被替换为\n的解决方案
  • Python python-nmap多线程端口扫描脚本(转载)
  • 通过curl得到http各阶段的响应时间
  • 深入浅出理解分类(Category)和类扩展(Extension)
  • 高阶函数
  • Javascript调试命令——你只会Console.log() ?
  • oracle impdp将导出用户的所有对象导入至另一个用户下,生成的触发器语句问题处理...
  • windows安装 pip
  • 一起学JAVA之《spring boot》03 - 开始spring boot基本配置及项目结构(转)
  • centos5中添加163yum源
  • js便签笔记(7)——style、currentStyle、getComputedStyle区别介绍【转载】
  • 都说要用人工智能改变医疗行业,不过……医生怎么看?
  • SpingCloudBus整合RabbitMQ
  • 10分钟了解Android项目构建流程
  • 条件变量使用总结
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 230. Kth Smallest Element in a BST
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • Solarized Scheme
  • Spring框架之我见(三)——IOC、AOP
  • tweak 支持第三方库
  • Vue2.x学习三:事件处理生命周期钩子
  • Vue官网教程学习过程中值得记录的一些事情
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 使用权重正则化较少模型过拟合
  • 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • (7)STL算法之交换赋值
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • (一)为什么要选择C++
  • .NET gRPC 和RESTful简单对比
  • .net 流——流的类型体系简单介绍
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .vollhavhelp-V-XXXXXXXX勒索病毒的最新威胁:如何恢复您的数据?
  • @staticmethod和@classmethod的作用与区别
  • []常用AT命令解释()
  • [bzoj 3534][Sdoi2014] 重建
  • [C/C++]数据结构 栈和队列()
  • [iOS]如何删除工程里面用cocoapods导入的第三方库
  • [Java][Android][Process] 暴力的服务能够解决一切,暴力的方式运行命令行语句
  • [LeetCode]Pow(x,n)
  • [linux c]linux do_div() 函数用法
  • [Mvc]在ASP.NET MVC中使用Repeater
  • [OS-Linux] CentOS 7.x 使用密钥登录安全设置
  • [POJ2446] Chessboard(二分图最大匹配-匈牙利算法)
  • [Ruby] 基础知识
  • [sqlmap]学习笔记#1
  • [week5]每周总结与工作计划
  • [国家集训队2012]middle
  • [技巧]读入优化
  • [技术选型] spring boot