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

news2024/10/7 4:32:13

对象复制与拷贝

文章目录

  • 对象复制与拷贝
    • 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];
}
@end

int 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];
}
@end

int 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];
}
@end

int 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 协议。否则,编译器会报错。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【python】将json内解码失败的中文修改为英文(‘utf-8‘ codec can‘t decode,labelme标注时文件名未中文)

出现问题的场景&#xff1a; 语义分割数据集&#xff0c;使用labelme工具进行标注&#xff0c;然后标注图片存在中文名&#xff0c;导致json标签文件写入中文图片名&#xff0c;从而解析失败。 代码解析json文件时&#xff0c;出现报错&#xff1a; python脚本需求&#x…

Java 7大排序

&#x1f435;本篇文章将对数据结构中7大排序的知识进行讲解 一、插入排序 有一组待排序的数据array&#xff0c;以升序为例&#xff0c;从第二个数据开始&#xff08;用tmp表示&#xff09;依次遍历整组数据&#xff0c;每遍历到一个数据都再从tmp的前一个数据开始&#xff0…

论文解读--High-resolution Automotive Radar Point Cloud Imaging and Processing

高分辨汽车雷达点云成像和处理 摘要 汽车雷达具有体积小、硬件成本低、全天候工作、高分辨率等公认的优点&#xff0c;是高级驾驶辅助系统(ADAS)必不可少的一类重要传感器。然而&#xff0c;低角度分辨率和低成像性能的限制很难满足下一阶段ADAS的需要。新兴的4D成像雷达采用多…

【随笔】Git 高级篇 -- 不带 source 参数的命令 git fetch git push(三十九)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

【工具】如何提取一个mp4文件的关键帧

文章目录 怎么做如何安装ffmepgUbuntu 或 DebianCentOS 或 FedoramacOSWindows其他 Linux 发行版 实践什么是关键帧 怎么做 你可以使用ffmpeg这个强大的多媒体处理工具来提取mp4文件中的关键帧。以下是一个示例命令&#xff0c;可以使用ffmpeg从mp4文件中提取关键帧&#xff1…

一款免费的PDF转换工具分享

最近在吾爱上发现一款PDF免费转换工具&#xff0c;支持多种格式转换&#xff0c;试了一下&#xff0c;还不错 最重要的是免费&#xff0c;不用开会员转换&#xff0c;也没有限制&#xff08;文末有工具地址&#xff09; ps:转换完成后看一下是否符合&#xff0c;可能会有些许…

哇哦,一个超级牛逼的图片格式!!使用它之后我们系统加载图片快了一倍!!! 图片格式转换webp学习!

什么是webp格式&#xff1f; WebP 格式是一种图像文件格式。 它是由谷歌开发的&#xff0c;旨在提供一种高效的图像压缩方式&#xff0c;同时保持较好的图像质量。WebP 格式具有较小的文件体积&#xff0c;能够在一定程度上减少网络传输的数据量&#xff0c;提升网页加载速度…

网工常用工具——Xshell

今天给各位介绍一下&#xff0c;Xshell工具 Xshell是一款功能强大的终端模拟器&#xff0c;主要用于Windows操作系统&#xff0c;用于远程访问和管理服务器&#xff0c;允许用户通过SSH&#xff08;Secure Shell&#xff09;协议安全地连接到远程Linux/Unix服务器或其他支持SS…

DenseCLIP论文讲解

文章目录 简介方法总体框架 &#xff08;Language-Guided Dense Prediction&#xff09;上下文感知提示 &#xff08;Context-Aware Prompting&#xff09;应用实例 论文&#xff1a;DenseCLIP: Language-Guided Dense Prediction with Context-Aware Prompting 代码&#xff1…

Spring与AI结合-spring boot3整合AI组件

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 目录 写在前面 spring ai简介 单独整合al接口 整合Spring AI组件 起步条件 ​编辑 进行必要配置 写在最后 写在前面 本文介绍了springboot开发后端服务中&#xff0c;AI组件(Spring A…

淘宝扭蛋机小程序:开启惊喜购物的新纪元

一、引言 随着科技的飞速发展&#xff0c;我们的购物方式也在不断地更新换代。淘宝&#xff0c;作为国内领先的电商平台&#xff0c;始终致力于为用户提供更加便捷、有趣的购物体验。为了满足广大用户对于新鲜、刺激购物体验的追求&#xff0c;淘宝特别推出了扭蛋机小程序&…

[C++] const 成员函数

标题&#xff1a;[C] this指针 & const 成员函数 水墨不写bug 正文开始&#xff1a; 目录 &#xff08;一&#xff09;Cpp的面向对象编程 &#xff08;二&#xff09;this指针 &#xff08;三&#xff09;const修饰的成员函数 在正式讲解const修饰成员函数之前&#x…

实测ChatGPT插件真香用法!视频一键变脑图,高数作业轻松拿捏

Sam Altman诚不我欺&#xff1a; ChatGPT插件和联网功能终于在这两天向尊贵的Plus用户全面开放了。 像之前预告的一样&#xff0c;联网可以直接获取2021年9月之后的消息&#xff0c;插件也安排了70多个&#xff0c;购物订餐订机票等日常功能应有尽有&#xff0c;更专业的数学、…

网站未部署证书有何影响,如何解决?

如果您的网站没有ssl证书会有以下风险 1 浏览器标记为不安全 未安装证书的网站在访问时会有不安全的提示弹窗或者在网址栏直接显示不安全 2 影响企业信誉 当用户访问网站时看到不安全提示&#xff0c;会对网站的真实性和安全性产生怀疑&#xff0c;不敢轻易与该企业合作&…

Python-VBA函数之旅-setattr函数

目录 一、setattr函数的常见应用场景 二、setattr函数使用注意事项 三、如何用好setattr函数&#xff1f; 1、setattr函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a; https://blog.csdn.net/ygb_1024?…

宏的优缺点?C++有哪些技术替代宏?(const)权限的平移、缩小

宏的优缺点&#xff1f; 优点&#xff1a; 1.增强代码的复用性。【减少冗余代码】 2.提高性能&#xff0c;提升代码运行效率。 缺点&#xff1a; 1.不方便调试宏。&#xff08;因为预编译阶段进行了替换&#xff09; 2.导致代码可读性差&#xff0c;可维护性差&#xff0…

小阳的戒S笔记

文章目录 写在前面2024年5月8日21:12:172024年5月9日21:48:242024年5月10日08:04:141、记录昨夜之身体变化2、自身制定之计划1.此亦乃要事&#xff0c;特定问了度娘与GPT&#xff0c;找时间还得咨询专业医师。2.通过跑步宣泄&#xff0c;同时锻炼身体3.我不会有压力&#xff0c…

替换spring-boot中的组件版本

spring-boot是一个用于简化开发的框架&#xff0c;引入spring-boot后会自动包含spring框架&#xff0c;通过引入xxx-start来完成指定组件的功能。比如&#xff1a; spring-boot-starter-web(嵌入 Tomcat 和 web 开发需要的 servlet 和 jsp 支持)spring-boot-starter-data-jpa(…

逻辑卷管理-LVM

目录 1. LVM的基本概念 2. Linux下创建和管理LVM 3. 环境准备 4. 物理卷管理 4.1. 创建物理卷 4.2. 显示物理卷 4.3. 删除物理卷 4. 卷组管理 4.1. 创建卷组 4.2. 显示卷组 4.3. 扩展卷组 4.4. 缩减卷组 4.5. 删除卷组 4.6. 分割卷组 4.7 组合卷组 5. 逻辑卷管…

VisualGDB:Linux静态库项目创建、编译及库的使用

接上篇《VisualGDB&#xff1a;Linux动态库项目创建、编译及库的使用》&#xff0c;静态库的创建和使用与动态库基本无差别&#xff0c;唯一需要做的就是指定项目生成静态库。 一、指定项目生成静态库 二、重新构建和编译项目 这里注意&#xff0c;同样要copy一个libxxx.so格式…