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

Objective-C的对象复制与拷贝选项

对象复制与拷贝

文章目录

  • 对象复制与拷贝
    • copy与mutablecopy
      • copy与mutablecopy的简介
      • 示例:
        • 不可变对象的复制
        • 可变对象的复制
    • NSCopying和NSMutableCopying协议
    • 深复刻和浅复刻
      • 浅拷贝(Shallow Copy):
      • 深拷贝(Deep Copy):
    • setter方法的复制选项

copy与mutablecopy

copy与mutablecopy的简介

copymutablecopy都是NSObeject所提供用来复制对象的方法,但它们有着不同的行为,主要区别如下:

  1. copy 方法:
  • copy 方法用于创建一个不可变的副本,无论原始对象是可变的还是不可变的。
  • 对于不可变对象,copy 方法仅仅是增加了引用计数,返回的仍然是原始对象本身。
  • 对于可变对象,copy 方法会创建一个新的不可变对象,内容与原始对象相同。

mutableCopy 方法:

  • mutableCopy 方法用于创建一个可变的副本,无论原始对象是可变的还是不可变的。
  • 对于不可变对象,mutableCopy 方法会创建一个新的可变对象,内容与原始对象相同。
  • 对于可变对象,mutableCopy 方法会创建一个新的可变对象,内容与原始对象相同。

总结一下,主要区别在于:

  • copy 返回的是不可变副本,而 mutableCopy 返回的是可变副本。
  • copy 对不可变对象和可变对象的行为不同,而 mutableCopy 则对所有对象的行为都一致。

示例:

不可变对象的复制
#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {@autoreleasepool {NSString* str1 = @"deer";NSString* str2 = str1;NSString* strcopy = [str1 copy];NSString* strmutablecopy = [str1 mutableCopy];NSLog(@"%p",str1);NSLog(@"%p",str2);NSLog(@"%p",strcopy);NSLog(@"%p",strmutablecopy);}return 0;
}

image-20240507192642271

不难看出来,str1,str2, strcopy ,指向的都为同一内存区域,为浅拷贝,由于mutablecopy生成了新的NSMutableString所以系统为其重新开辟一块空间,进行了深拷贝。

可变对象的复制
#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {@autoreleasepool {NSMutableString *mstr = [NSMutableString stringWithString:@"hello"];NSString *mstrcopy1 = [mstr copy];NSMutableString *mstrcopy2 = [mstr copy];NSMutableString *mstrmutablecopy = [mstr mutableCopy];[mstr appendString:@",aa"];//[mstrcopy2 appendString:@",bb"];[mstrmutablecopy appendString:@",cc"];NSLog(@"%p",mstr);NSLog(@"%p",mstrcopy1);NSLog(@"%p",mstrcopy2);NSLog(@"%p",mstrmutablecopy);}return 0;
}

对于注释的那一句代码,由于mstrcopy是通过copy方法得来,类型为NSString是不可变的,所以后面无法进行追加。可以发现通过copy得到的字符串指向的是同一片地址,但是复制得到的地址相对于原来的mstr都是不同的,所以我们可以知道对于可变对象进行copymutablecopy都是进行深拷贝。

NSCopying和NSMutableCopying协议

虽然NSObject虽然都给出了copymutablecopy的方法,但是不一定在任何时候都能够使用,为了确保一个对象可以调用copy方法复制得到自身的不可变副本,我们需要做如下事情

  • 让该类实现NSCopying协议
  • 让该类实现copyWithZone方法

对于mutablecopy也是同理

  • 实现NSMutableCopying协议
  • 让该类实现mutableCopyWithZone方法
#import <Foundation/Foundation.h>@interface MyObject : NSObject <NSCopying>@property (nonatomic, strong) NSMutableString *name;
@property (nonatomic, assign) int age;@end@implementation MyObject- (id)copyWithZone:(NSZone *)zone {MyObject *copy = [[[self class] allocWithZone:zone] init];// 递归复制子对象copy.name = self.name;copy.age = self.age;return copy;
}-(NSString*)description {return [NSString stringWithFormat:@"name = %@, age = %d", self.name, self.age];
}
@endint main(int argc, const char * argv[]) {@autoreleasepool {// 创建原始对象MyObject *org = [MyObject new];org.name = [NSMutableString stringWithString:@"aa"];org.age = 10;NSLog(@"%@",org);MyObject *copy1 = [org copy];NSLog(@"%@",copy1);copy1.name = [NSMutableString stringWithString:@"bb"];copy1.age = 20;NSLog(@"%@",org);NSLog(@"%@",copy1);}return 0;
}

从以上程序我们看出来,我们对org进行一次副本复制,对得到的copy的成员变量进行赋值,对org不会产生影响。

image-20240507221647973

#import <Foundation/Foundation.h>@interface MyObject : NSObject <NSCopying>@property (nonatomic, strong) NSMutableString *name;
@property (nonatomic, assign) int age;@end@implementation MyObject- (id)copyWithZone:(NSZone *)zone {MyObject *copy = [[[self class] allocWithZone:zone] init];// 递归复制子对象copy.name = self.name;copy.age = self.age;return copy;
}-(NSString*)description {return [NSString stringWithFormat:@"name = %@, age = %d", self.name, self.age];
}
@endint main(int argc, const char * argv[]) {@autoreleasepool {// 创建原始对象MyObject *org = [MyObject new];org.name = [NSMutableString stringWithString:@"aa"];org.age = 10;NSLog(@"%@",org);MyObject *copy1 = [org copy];NSLog(@"%@",copy1);}return 0;
}

深复刻和浅复刻

深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是在编程中经常遇到的两个概念,它们描述了在复制对象时复制的内容的不同程度。

浅拷贝(Shallow Copy):

浅拷贝是指将一个对象复制到一个新的对象中,即将对内存指针的复制,并增加其引用计数。如果对象包含其他对象的引用,浅拷贝将不会复制这些引用的对象,而是将其引用复制到新对象中。因此,新对象和原对象中的引用指向相同的对象。如果其中一个对象修改了共享对象,另一个对象也会受到影响。当使用A指针改变内容时,B指针指向的内容也会跟着改变

image-20240507191217818

深拷贝(Deep Copy):

深拷贝是指将一个对象递归复制到一个新的对象中,开辟一片新的空间,并且复制所有引用的对象,直至没有任何共用的部分。这意味着,新对象中的每个对象都是原始对象的副本,而不是共享的。因此,如果其中一个对象修改了它指针所指向的对象,另一个对象不会受到影响。

image-20240507191437571

对于上图来说,分配了新的内存,改变A指针指向的值,不会影响到指针B指向值的内容。

通常情况下,浅拷贝的效率比深拷贝高,因为它不需要递归地复制所有引用的对象。但是,在需要保持对象之间的独立性时,深拷贝是必需的。

我们再来探究一下我们刚刚看到的例子,

#import <Foundation/Foundation.h>@interface MyObject : NSObject <NSCopying>@property (nonatomic, strong) NSMutableString *name;
@property (nonatomic, assign) int age;@end@implementation MyObject- (id)copyWithZone:(NSZone *)zone {MyObject *copy = [[[self class] allocWithZone:zone] init];// 递归复制子对象copy.name = self.name;copy.age = self.age;return copy;
}-(NSString*)description {return [NSString stringWithFormat:@"name = %@, age = %d", self.name, self.age];
}
@endint main(int argc, const char * argv[]) {@autoreleasepool {// 创建原始对象MyObject *org = [MyObject new];org.name = [NSMutableString stringWithString:@"aa"];org.age = 10;NSLog(@"%@",org);MyObject *copy1 = [org copy];NSLog(@"%@",copy1);[copy1.name replaceCharactersInRange:NSMakeRange(0,2) withString:@"bb"];copy1.age = 20;NSLog(@"%@",copy1);NSLog(@"%@",org);}return 0;
}

我们的程序输出以下结果。

似乎很奇怪,我们通过repalce的函数去修改字符串,使得orgcopy的字符串内容都发生了改变,想要解决这个问题我们可以画一个图来辅助我们进行理解。

image-20240508200450863

对于即将被复制的org来说,变量name之中存储的是字符串的地址指针,而不是字符串本身,如果进行copy.name = self.name; copy.age = self.age;,那么只是将字符串的地址赋给copy之中的name属性,实际上两者指针都指向一个相同的NSMutableString类型字符串。

image-20240508201004905

对于这种复制模式我们就称之为浅复制,在内存中复制一个对象,但是原来的和复制得到的内容指向一个相同的对象,也就是说两个对象都存在依然存在的部分

那如果要实现深复制呢?前面说过深复制不仅会对对象本身进行复制,而且还会"递归"复制每一个指针的属性,直到两者没有共同的部分。我们将copyWithZone的代码进行一些修改就能实现深复制的原理。

- (id)copyWithZone:(NSZone *)zone {MyObject *copy = [[[self class] allocWithZone:zone] init];// 递归复制子对象copy.name = [self.name mutableCopy];copy.age = self.age;return copy;
}

我们将原对象的属性值复制了一份可变的副本,再将新的可变副本的内容赋值给新对象的name属性,与原先的对象再无共用的部分,实现了深复制

一般来说,实现深复制的难度很大,对于包含大量指针属性的对象来说,更或者对象中的变量中又嵌套了一层指针的话,那会更加复杂

setter方法的复制选项

当属性声明为 copy 时,在 setter 方法中会执行深复制操作。这意味着在设置属性值时,会创建一个属性值的副本,而不是直接引用传入的对象。

考虑以下代码:

@property (nonatomic, copy) NSString *name;

对应的 setter 方法会类似于这样:

- (void)setName:(NSString *)name {if (_name != name) {_name = [name copy];}
}

在这个 setter 方法中,首先进行了对象的比较,确保新值和旧值不是同一个对象。然后,使用 copy 方法来创建新值的副本,并将副本赋值给属性 _name。这样做可以确保属性值的安全性,防止外部对象对属性值的意外修改。

copy 操作实际上是执行了浅复制,它只会复制对象的指针,而不会复制对象本身。这意味着它会创建一个新的指针,指向相同的内存地址,而不是创建一个完全独立的副本。

对于不可变对象,由于其内容是不可变的,因此可以安全地使用 copy 属性。对于可变对象而言,使用copy可能会导致意外的行为。因为修改一个对象会影响到所有指向该对象的指针。这就是为什么使用 copy 属性时需要小心,特别是。另外,如果属性值是可变对象,那么每次设置属性值时都会创建一个新的不可变副本,这样会导致性能开销增加。

需要注意的是,copy 属性通常适用于容器类对象和其他可复制的对象,但对于自定义类对象,需要确保其实现了 NSCopying 协议。否则,编译器会报错。

相关文章:

Objective-C的对象复制与拷贝选项

对象复制与拷贝 文章目录 对象复制与拷贝copy与mutablecopycopy与mutablecopy的简介示例&#xff1a;不可变对象的复制可变对象的复制 NSCopying和NSMutableCopying协议深复刻和浅复刻浅拷贝&#xff08;Shallow Copy&#xff09;&#xff1a;深拷贝&#xff08;Deep Copy&…...

HTML5 中的离线缓存机制,即应用缓存(Application Cache 或 AppCache)已被废弃并正在被逐步移除

HTML5 中的离线缓存机制&#xff0c;即应用缓存&#xff08;Application Cache 或 AppCache&#xff09;已被废弃并正在被逐步移除。这是因为应用缓存存在一些设计上的缺陷和限制&#xff0c;导致它在实际应用中经常出现问题。 取而代之的是一种更强大、更灵活的技术——Servi…...

vue3+ant design实现表格数据导出Excel

提示:实现表格数据导出Excel 文章目录 前言 一、安装ant design? 二、引用ant design 1.搭建框架 2.获取表格数据 三、封装导出表格的代码 四、导出 1.获取导出地址 2.在下载导出事件中添加导出代码 五、全部代码 前言 今天终于有时间来更新文章了,最近公司项目比较紧…...

VBA_NZ系列工具NZ06:VBA创建PDF文件说明

我的教程一共九套及VBA汉英手册一部&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到数据库&#xff0c;到字典&#xff0c;到高级的网抓及类的应用。大家在学习的过程中可能会存在困惑&#xff0c;这么多知识点该如何组织…...

Git === Git概述 Git安装

第1章 Git概述 Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。 Git易于学习&#xff0c;占地面积小&#xff0c;性能极快。 它具有廉价的本地库&#xff0c;方便的暂存区域和多个工作流分支等特性。其性能优于Subversion…...

Linux diff命令(比较两个文件或目录的内容差异)

文章目录 Linux diff 命令详解教程基本用法比较文件输出解释 递归比较&#xff08;-r&#xff09;示例代码 控制输出格式统一格式&#xff08;-u&#xff09;上下文格式&#xff08;-c&#xff09; 高级选项忽略所有空白差异&#xff08;-w&#xff09;仅报告文件是否不同 Linu…...

从传统到现代:水表的远程抄表革命

1.引言&#xff1a;技术驱动的转型 在过去的几十年里&#xff0c;我们的生活方式被科技的快速发展深深影响&#xff0c;其中就包括了公用设施的管理方式。传统水表的远程抄表系统就是这样一个例子&#xff0c;它将老旧的手动抄表模式转变为高效、精确的自动化系统。 2.传统水…...

视频怎么打水印?6个软件教你快速进行视频水印制作

视频怎么打水印&#xff1f;6个软件教你快速进行视频水印制作 添加水印是保护视频版权、提升视频专业性的重要手段之一。以下是六款软件&#xff0c;它们能够帮助你快速进行视频水印制作&#xff0c;让你的视频更具个性和专业性&#xff1a; 1.迅捷视频剪辑软件&#xff1a;…...

面试 Java 基础八股文十问十答第二十八期

面试 Java 基础八股文十问十答第二十八期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01;关注专栏后就能收到持续更新&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1&#xff09;动态代理是什么&am…...

Excel-VBA报错01-解决方法

【已删除的部件:部件/xl/vbaProject.bin。(Visual Basic for Applications(VBA))】 1.问题复现&#xff1a; Win10 &#xff1b;64位 &#xff1b;Office Excel 2016 打开带有宏的Excel文件&#xff0c;报错&#xff1a;【已删除的部件&#xff1a;部件/xl/vbaProject.bin。…...

php利用阿里云短信SDK实现短信发送功能

当使用PHP结合阿里云短信服务SDK来实现短信验证码登录时&#xff0c;你需要遵循以下步骤&#xff1a; 1. 注册阿里云账号并开通短信服务 首先&#xff0c;你需要有一个阿里云账号&#xff0c;并在阿里云控制台中开通短信服务&#xff08;Dysmsapi&#xff09;。 2. 获取Acce…...

承装(修、试)电力工程施工许可证四级资质可以承接多大的项目?

承装&#xff08;修、试&#xff09;电力工程施工许可证四级资质可以承接多大的项目&#xff1f; 承装&#xff08;修、试&#xff09;电力工程施工许可证四级资质可以承接的项目规模及范围是一个复杂且细致的问题&#xff0c;涉及电力工程施工的多个方面。根据四级资质的相关规…...

影像图层调整图像显示效果的色彩参数汇总

在Cesium的ImageryProvider中&#xff0c;以下图层对象支持调整图像显示效果的色彩参数&#xff1a; - ArcGisMapServerImageryProvider - BingMapsImageryProvider - GoogleEarthEnterpriseImageryProvider&#xff08;如果服务支持&#xff09; - TileMapServiceImager…...

EasyHPC - PyTorch入门教程【笔记】

内容来源&#xff1a;超算习堂 (easyhpc.net) 文章目录 01 Tensors环境要求1.1 Tensors1.1.1 直接创建tensor1.1.2 在现有tensor中创建tensor1.1.3 从NumPy中创建tensor 1.2 基本运算1.2.1 使用运算符1.2.2 调用方法 1.3 CUDA Tensors 02 Autograd2.1 Tensor2.2 Gradient 03 Ne…...

Node.js里面 Path 模块的介绍和使用

Node.js path 模块提供了一些用于处理文件路径的小工具&#xff0c;我们可以通过以下方式引入该模块&#xff1a; var path require("path") 方法描述 序号方法 & 描述1path.normalize(p) 规范化路径&#xff0c;注意.. 和 .。2path.join([path1][, path2][,…...

【Linux】Centos7配置JDK

1.启动虚拟机、Xshell、Xftp 2.在Xshell中新建一个会话&#xff0c;用于连接到虚拟机中 3.因为虚拟机里自带有JDK&#xff0c;所以需要先卸载自带的JDK 3.1.查询已安装的 jdk 列表 rpm -qa | grep jdk3.2.将查询到的全部删除 yum -y remove XXX&#xff08;上面查询到的 j…...

pytorch中统计一个数在tensor中出现了几次

pytorch中统计一个数在tensor中出现了几次 在PyTorch中&#xff0c;可以使用torch.eq()函数配合torch.sum()来统计某个数值在Tensor中出现的次数。torch.eq()函数会返回一个新的Tensor&#xff0c;其中对于每个元素来说&#xff0c;如果和指定的数值相等&#xff0c;则该位置为…...

a-auto-complete 请求后端数据做模糊查询,解决下拉框选择选不上,不回显的问题

a-auto-complete 请求后端数据做模糊查询&#xff0c;解决下拉框选择选不上&#xff0c;不回显的问题 记录一个a-auto-complete卡bug卡了两天&#xff0c;找不到哪里的问题下拉框选择选不上&#xff0c;不回显&#xff0c;最后终于解决了。 我还对下拉框显示的内容做了小调整。…...

Leetcode—724. 寻找数组的中心下标【简单】

2024每日刷题&#xff08;129&#xff09; Leetcode—724. 寻找数组的中心下标 实现代码 class Solution { public:int pivotIndex(vector<int>& nums) {int sum accumulate(nums.begin(), nums.end(), 0);int prefix 0;for(int i 0; i < nums.size(); i) {i…...

C语言 | Leetcode C语言题解之第72题编辑距离

题目&#xff1a; 题解&#xff1a; static inline int Min(const int a, const int b, const int c) {int min (a < b) ? a : b;return (min < c) ? min : c; }int minDistance(char * word1, char * word2){int m strlen(word1), n strlen(word2);int dp[m 1][n…...

别再被空白页坑了!用html2canvas + print-js打印Vue/React组件,保姆级避坑指南

彻底解决前端组件打印难题&#xff1a;html2canvas与print-js深度整合实践 在管理后台、数据报表等企业级应用中&#xff0c;精确打印特定组件是刚需&#xff0c;但现代前端框架的组件化特性让这个"简单需求"变得异常棘手。当你的Vue/React组件在屏幕上完美呈现&…...

玩转ESP32-S3调试:GDB高级命令与自定义调试技巧大全

玩转ESP32-S3调试&#xff1a;GDB高级命令与自定义调试技巧大全 调试嵌入式系统时&#xff0c;GDB的强大功能往往被低估。对于ESP32-S3开发者来说&#xff0c;掌握GDB的高级调试技巧可以显著提升解决复杂问题的效率。本文将深入探讨如何利用GDB的watch命令、自定义命令、跳转执…...

管道应力理论(应用)

本文仅对管道应力涉及的理论知识&#xff08;偏向于应用&#xff09;进行简单介绍。管道应力&#xff1a;对管道应力校核是为了防止管壁内应力过大对管道造成破坏&#xff0c;不同的荷载引起不同类型的应力&#xff0c;在实际工程应用中&#xff0c;一般分为三种&#xff1a;一…...

告别NMS!用RT-DETR在1080Ti上跑出108FPS的实时目标检测(保姆级部署教程)

在1080Ti上实现108FPS的RT-DETR实时目标检测实战指南 当目标检测遇上Transformer架构&#xff0c;一场关于速度与精度的革命正在悄然发生。RT-DETR作为DETR家族的最新成员&#xff0c;不仅继承了端到端集合预测的基因&#xff0c;更通过一系列创新设计突破了实时检测的瓶颈。本…...

基因组变异致病性预测:从SIFT、PolyPhen到PrimateAI的算法演进

点击 “AladdinEdu&#xff0c;你的AI学习实践工作坊”&#xff0c;注册即送-H卡级别算力&#xff0c;沉浸式云原生集成开发环境&#xff0c;80G大显存多卡并行&#xff0c;按量弹性计费&#xff0c;教育用户更享超低价。 摘要&#xff1a;基因组变异致病性预测是精准医学的关键…...

IDK slgA:无创检测,便捷采样

在人体的防御体系中&#xff0c;免疫系统扮演着至关重要的角色。而其中&#xff0c;黏膜免疫系统则是抵御外界病原体的第一道防线。在众多免疫成分中&#xff0c;分泌型免疫球蛋白A&#xff08;Secretory Immunoglobulin A, 简称sIgA&#xff09;以其独特的功能和广泛的存在形式…...

sinx/x在0到无穷积分的条件收敛性分析与证明

1. 从物理现象到数学问题&#xff1a;为什么研究sinx/x的积分&#xff1f; 我第一次接触sinx/x的积分是在信号处理课程中&#xff0c;这个看似简单的函数在傅里叶变换和频谱分析中扮演着关键角色。工程师们用它来描述理想低通滤波器的频率响应&#xff0c;物理学家则在衍射现象…...

解锁ComfyUI扩展潜能:工作流优化实战指南

解锁ComfyUI扩展潜能&#xff1a;工作流优化实战指南 【免费下载链接】ComfyUI-Custom-Scripts Enhancements & experiments for ComfyUI, mostly focusing on UI features 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-Custom-Scripts 在AI绘画创作中&…...

VS2022 + WinForms:从拖控件到写逻辑,手把手带你做出第一个C#计算器

VS2022 WinForms&#xff1a;从拖控件到写逻辑&#xff0c;手把手带你做出第一个C#计算器 第一次打开Visual Studio 2022时&#xff0c;那个闪亮的启动界面可能会让你既兴奋又不知所措。作为微软最新的集成开发环境&#xff0c;VS2022为C#开发者提供了强大的工具链&#xff0…...

万象视界灵坛代码实例:使用Gradio快速搭建像素风Web UI,零前端开发经验可用

万象视界灵坛代码实例&#xff1a;使用Gradio快速搭建像素风Web UI&#xff0c;零前端开发经验可用 1. 项目概述 万象视界灵坛是一款基于OpenAI CLIP模型的多模态智能感知平台&#xff0c;它将复杂的语义对齐功能包装在充满游戏感的像素风界面中。这个项目最大的特点是完全不…...