[OC]深拷贝与浅拷贝

深拷贝与浅拷贝

深拷贝与浅拷贝

  • 深拷贝与浅拷贝
    • 定义
    • 按照类型说明
      • 非容器类对象的深拷贝与浅拷贝
        • 不可变字符串
        • 可变类型字符串
      • 容器类对象的深浅拷贝
      • 自定义类对象的深浅拷贝
      • 容器类对象的完全深拷贝
        • 1.copyItems
        • 2.解档和归档

定义

深拷贝:简单来说就是创建一个与被复制对象的值完全相同的对象,但是他们的地址不同。

浅拷贝:浅拷贝就是仅仅创建一个存放复制对象相同地址的指针变量

按照类型说明

非容器类对象的深拷贝与浅拷贝

这里我们从字符串来作为例子。

不可变字符串
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString* str1 = @"dddddd";
        NSString* str2 = [str1 copy];
        NSString* str3 = [str1 mutableCopy];
        NSMutableString* str4 = [str1 copy];
        NSMutableString* str5 = [str1 mutableCopy];
        NSLog(@"str1:%p", str1);
        NSLog(@"str2:%p", str2);
        NSLog(@"str3:%p", str3);
        NSLog(@"str4:%p", str4);
        NSLog(@"str5:%p", str5);
    }
    return 0;
}

下面的打印结果为:

在这里插入图片描述

我们可以发现一个事情就是str1和str2和str4三者的地址是相同的。所以可以得出一个结论:就是如果是字符串常量的话,只要是copy就是浅拷贝,mutableCopy就是深拷贝。tips:我们用[NSString stringWithstring:]方式创建的也是一个常量区字符串。

可变类型字符串
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableString* str1 = [NSMutableString stringWithString:@"helloworld"];
        NSMutableString* str2 = [str1 copy];
        NSMutableString* str3 = [str1 mutableCopy];
        NSString* str4 = [str1 copy];
        NSString* str5 = [str1 mutableCopy];
        NSLog(@"str1:%p", str1);
        NSLog(@"str2:%p", str2);
        NSLog(@"str3:%p", str3);
        NSLog(@"str4:%p", str4);
        NSLog(@"str5:%p", str5);
    }
    return 0;
}

打印结果如下:

在这里插入图片描述

这里我们发现这里的两种拷贝都是深拷贝,它都重新创建了一块新的区域存储这部分内容。

因此可以得出一个结论:

  1. 可变对象copy后的对象是不可变的,mutableCopy后的对象是可变的。
  2. 对于可变对像的复制都是深拷贝,

复制后的对象类型仅仅和我们的对象的选择的复制方式有关。

容器类对象的深浅拷贝

NSArray:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray* ary1 = [NSArray arrayWithObjects:@"krystal", @"xiyang", @"zzr", nil];
        NSArray* ary2 = [ary1 copy];
        NSArray* ary3 = [ary1 mutableCopy];
        NSMutableArray* ary4 = [ary1 copy];
        NSMutableArray* ary5 = [ary1 mutableCopy];
        NSLog(@"arry1:%p", ary1);
        NSLog(@"arry2:%p", ary2);
        NSLog(@"arry3:%p", ary3);
        NSLog(@"arry4:%p", ary4);
        NSLog(@"arry5:%p", ary5);
    }
    return 0;
}

打印结果如下:
在这里插入图片描述

NSMutableArray:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray* ary1 = [NSMutableArray arrayWithObjects:@"krystal", @"xiyang", @"zzr", nil];
        NSArray* ary2 = [ary1 copy];
        NSArray* ary3 = [ary1 mutableCopy];
        NSMutableArray* ary4 = [ary1 copy];
        NSMutableArray* ary5 = [ary1 mutableCopy];
        NSLog(@"arry1:%p", ary1);
        NSLog(@"arry2:%p", ary2);
        NSLog(@"arry3:%p", ary3);
        NSLog(@"arry4:%p", ary4);
        NSLog(@"arry5:%p", ary5);
    }
    return 0;
}

打印结果如下:
在这里插入图片描述

这里可以发现其实两者和字符串类型类似,也是遵循可变的copy和mutableCopy都是深拷贝,而不可变类型则copy是浅拷贝而mutableCopy是深拷贝。

但是这里我们还没有考虑到有关容器内部元素的深浅拷贝的一个情况。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray* ary1 = [NSMutableArray arrayWithObjects:@"krystal", @"xiyang", @"zzr", nil];
        NSArray* ary2 = [ary1 copy];
        NSArray* ary3 = [ary1 mutableCopy];
        NSMutableArray* ary4 = [ary1 copy];
        NSMutableArray* ary5 = [ary1 mutableCopy];
        NSLog(@"arry1:%p,ary1[0]:%p,ary1[1]:%p,ary1[2]:%p", ary1, ary1[0], ary1[1], ary1[2]);
        NSLog(@"arry2:%p,ary2[0]:%p,ary2[1]:%p,ary2[2]:%p", ary2, ary2[0], ary2[1], ary2[2]);
        NSLog(@"arry3:%p,ary3[0]:%p,ary3[1]:%p,ary3[2]:%p", ary3, ary3[0], ary3[1], ary3[2]);
        NSLog(@"arry4:%p,ary4[0]:%p,ary4[1]:%p,ary4[2]:%p", ary4, ary4[0], ary4[1], ary4[2]);
        NSLog(@"arry5:%p,ary5[0]:%p,ary5[1]:%p,ary5[2]:%p", ary5, ary5[0], ary5[1], ary5[2]);
    }
    return 0;
}

在这里插入图片描述

这里不难发现尽管我们是对于每一个容器进行了一个深拷贝,但是不难发现我们的所有元素仅仅只是被浅拷贝了,这里我们可以将它称为数组中元素的浅层的深拷贝。

Tip:我们数组中的元素实际上是对象的引用,所以我们访问数组的第一个元素的时候,不管是从原始数组还是从拷贝的不可变数组抑或是拷贝的可变数组中访问,都会得到同一个字符串对象的引用。

这句话的意思是,在数组中存储的元素实际上是指向对象的引用,而不是对象本身。当我们将一个对象添加到数组中时,实际上是将该对象的引用存储在数组的对应位置上。

自定义类对象的深浅拷贝

自定义类对象的拷贝需要我们的类别遵循NSCopying协议,

接口部分

容器类对象的完全深拷贝

1.copyItems

我们先来给出它的定义:
copyItems: 方法是 NSArray 和 NSMutableArray 的一个方法,用于创建一个新的数组,并对数组中的元素进行拷贝。该方法会遍历原始数组的每个元素,并对每个元素执行copy操作,然后将拷贝后的元素添加到新的数组中。

可以看到定义中的关键是遍历数组中的每个元素对其进行copy

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableString* str1;
        FKUser* p1 = [[FKUser alloc] initWithName:@"krystal" pass:@"12"];
        FKUser* p2 = [p1 copy];
        NSArray* ary = [NSArray arrayWithObjects:p1, p2, nil];
        NSArray* ary1 = [[NSArray alloc] initWithArray:ary copyItems:YES];
        NSLog(@"%p, %p", ary, ary1);
        NSLog(@"%p, %p", ary1[0], ary[0]);
      	FKUser* p3 = ary1[0];
        FKUser* p4 = ary[0];
        NSLog(@"%p, %p", p3.name, p4.name);
    }
    return 0;
}

打印结果为:

在这里插入图片描述

这个展示出我们对于数组内部的元素也进行一了一次深拷贝。这个方法实现了我们的对于容器内部的一个深拷贝。但是,要注意一个点,就是我们容器类对象中的元素是非容器类才可以实现一个复制,但是注意这里我们没有对对象进行更深一层的拷贝,如果我们想实现一个更深一次层次的拷贝则需要进行一递归拷贝的方式。

即:

- (id)copyWithZone:(NSZone*)zone {
    NSLog(@"对象复制");
    FKUser* newUser = [[[self class] allocWithZone:zone] init];
    newUser.name = [self.name mutableCopy];
    newUser.pass = [self.pass copy];
    return newUser;
}

修改了这部分代码后就可以得到下面的一个结果:

在这里插入图片描述

但是,要注意一个点,就是我们容器类对象中的元素是非容器类才可以实现一个复制,下面是后有关拷贝容器的内容

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableString* str1;
        FKUser* p1 = [[FKUser alloc] initWithName:@"krystal" pass:@"12"];
        FKUser* p2 = [p1 copy];
        NSArray* ary = [NSArray arrayWithObjects:p1, p2, nil];
        NSArray* ary1 = [NSArray arrayWithObject:ary];
        NSArray* ary2 = [[NSArray alloc] initWithArray:ary1 copyItems:YES];
        NSLog(@"%p, %p",ary1, ary2);
        NSLog(@"%p, %p, %p",ary1[0], ary2[0], ary);
    }
    return 0;
}

在这里插入图片描述

这里我们就会发现这里的对于容器的深拷贝过程失败了,我们这里还是仅仅实现了一次浅拷贝。

因此我们可以得出一个结论:该方法进行深拷贝仅仅只适用于数组的直接元素,不能对于数组中的容器对象进行一个拷贝。

2.解档和归档

我们在iOS中间可以通过解档和归档的方式实现一个完全意义上的一个完全拷贝,笔者也不是很理解相关的内容,只能简单给出对应的代码:

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:ary]];

以上就是有关深拷贝和浅拷贝的内容

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/647669.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

虚拟化技术[2]之存储虚拟化

存储虚拟化 存储虚拟化简介存储虚拟化一般模型存储虚拟化实现方式基于主机存储虚拟化基于存储设备存储虚拟化基于网络存储虚拟化 案例分析:VMFSVMFS功能 存储虚拟化简介 存储虚拟化:将存储网络中的各个分散且异构的存储设备按照一定的策略映射成一个统一…

webpack5生产模式

生产模式 生产模式准备 开发模式和生产模式有不同的 配置文件 2修改webpack.prod.js文件修改webpack.dev.js文件 修改webpack.dev.js文件 1》修改输出路径为undefined 2》将绝对路径进行修改,进行回退 此时文件的执行命令为 修改webpack.prod.js文件 1》修改绝…

LangChain笔记

很好的LLM知识博客: https://lilianweng.github.io/posts/2023-06-23-agent/ LangChain的prompt hub: https://smith.langchain.com/hub 一. Q&A 1. Q&A os.environ["OPENAI_API_KEY"] “OpenAI的KEY” # 把openai-key放到环境变量里&…

【Linux】Linux的基本指令_2

文章目录 二、基本指令8. man9. nano 和 cat10. cp11. mv12. echo 和 > 和 >> 和 <13. more 和 less14. head 和 tail 和 | 未完待续 二、基本指令 8. man Linux的命令有很多参数&#xff0c;我们不可能全记住&#xff0c;我们可以通过查看联机手册获取帮助。访问…

深入编程逻辑:从分支到循环的奥秘

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、编程逻辑的基石&#xff1a;分支与循环 分支逻辑详解 代码案例&#xff1a;判断整数是…

Unity 资源 之 限时免费的Lowpoly农场动物,等你来领!

Unity资源 之 Lowpoly farm animals 农村动物 前言资源包内容领取兑换码 前言 Unity 资源商店为大家带来了一份特别的惊喜——限时免费的农场动物资源&#xff01;这是一个充满趣味和实用性的资源包。 资源包内容 在这个资源包中&#xff0c;你可以找到丰富多样的低地养殖动物…

685. 冗余连接 II

685. 冗余连接 II 问题描述 在本问题中&#xff0c;有根树指满足以下条件的 有向 图。该树只有一个根节点&#xff0c;所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点&#xff0c;而根节点没有父节点。 输入一个有向图&#xff0c;该…

mac 安装Node.js

文章目录 前言一、Node是什么&#xff1f;二、下载三、安装四、验证总结 前言 Node.js是一个开源、跨平台的JavaScript运行时环境&#xff0c;它允许开发者在服务器端运行JavaScript代码。Node.js是基于Chrome V8 JavaScript引擎构建的&#xff0c;它的设计目标是提供一种高效…

这样的直男程序员,活该你单身一万年!

#分享下相亲时遇到过哪些奇葩现象# 这样的直男程序员&#xff0c;活该你单身一万年&#xff01; 在丛丛脱单小程序上相亲&#xff0c;遇到一个程序员妹纸&#xff0c;于是有了如下的真实故事&#xff1a; 妹子说她是程序员来着&#xff0c;想着我也是程序员&#xff0c;就想交…

基于xilinx FPGA的 FFT IP使用例程说明文档(可动态配置FFT点数,可计算信号频率与幅度)

目录 1 概述2 IP examples功能3 IP 使用例程3.1 IP设置3.2 fft_demo端口3.3 例程框图3.4 仿真结果3.5 仿真验证得出的结论4 注意事项5例程位置 1 概述 本文用于讲解xilinx IP 的FFT ip examples的功能说明&#xff0c;方便使用者快速上手。 参考文档&#xff1a;《PG109》 2 …

MySQL大表删除方案

1.问题 在生产环境中&#xff0c;执行大表删除操作时&#xff0c;很容易因为占用了大量io资源导致其他事务被阻塞&#xff0c;最终事务不断堆积导致MySQL挂掉。 2.drop命令 drop命令&#xff0c;MySQL主要干了两件事&#xff1a; 清除buffer pool缓冲&#xff08;内存&…

Java控制台实现斗地主的洗牌和发牌功能

一、题目要求 有3个玩家&#xff1a;A&#xff0c;B&#xff0c;C。底牌有三张牌&#xff0c;每个人共17张牌&#xff0c;共&#xff08;17*3354&#xff09;张牌&#xff0c;实现洗牌与发牌&#xff0c;只在控制没有实现UI可视化。 二、思路 1、用List集合存储所有的扑克牌。…

表查询基础【mysql】【表内容 增,删,改,查询】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;Linux_花果山~程序猿的博客-CSDN博客MySQL之旅_花果山~程序猿的博客-CSDN博客Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我…

MTK下载AP

只升级选Firemare Upgrade &#xff0c;点下载后&#xff0c;关机下插入USB

多线程案例(线程池)

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f649; 内容推荐:<计算坤是如何工作的>&#x1f649; &#x1f439;今日诗词:百年兴衰皆由人, 不由天&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&…

Android11热点启动和关闭

Android官方关于Wi-Fi Hotspot (Soft AP) 的文章&#xff1a;https://source.android.com/docs/core/connect/wifi-softap?hlzh-cn 在 Android 11 的WifiManager类中有一套系统 API 可以控制热点的开和关&#xff0c;代码如下&#xff1a; 开启热点&#xff1a; // SoftApC…

计算机设计大赛

目录 1.1需求分析 2.1概要设计 3.1软件界面设计&#xff1a; 4.1代码开源 1.1需求分析 1.1 产品开发本说明&#xff1a; 在如今每人都会扔出许多垃圾&#xff0c;在一些地方大部分垃圾能得到卫生填埋、焚烧等无害化处理&#xff0c;而更多的垃圾则是简单的掩埋&#xff…

3D牙科网格分割使用基于语义的特征学习与图变换器

文章目录 3D Dental Mesh Segmentation Using Semantics-Based Feature Learning with Graph-Transformer摘要方法实验结果 3D Dental Mesh Segmentation Using Semantics-Based Feature Learning with Graph-Transformer 摘要 本文提出了一种新颖的基于语义的牙科网格分割方…

计算机毕业设计 | SSM汽车租赁系统(附源码)

1&#xff0c; 概述 1.1 课题背景 随着社会的快速发展&#xff0c;计算机的影响是全面且深入的。用户生活水平的不断提高&#xff0c;日常生活中用户对汽车租赁系统方面的要求也在不断提高&#xff0c;需要汽车租赁系统查询的人数更是不断增加&#xff0c;使得汽车租赁系统的…

rockylinux 利用nexus 搭建私服yum仓库

简单说下为啥弄这个私服&#xff0c;因为自己要学习一些东西&#xff0c;比如新版的k8s等&#xff0c;其中会涉及到一些yum的安装&#xff0c;为了防止因网络问题导致yum安装失败&#xff0c;和重复下载&#xff0c;所以弄个私服&#xff0c;当然也有为了意外保障的想法&#x…