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

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

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

相关文章

AI智能分析高精度烟火算法EasyCVR视频方案助力打造森林防火建设

一、背景 随着夏季的来临&#xff0c;高温、干燥的天气条件使得火灾隐患显著增加&#xff0c;特别是对于广袤的森林地区来说&#xff0c;一旦发生火灾&#xff0c;后果将不堪设想。在这样的背景下&#xff0c;视频汇聚系统EasyCVR视频融合云平台AI智能分析在森林防火中发挥着至…

APScheduler定时器使用【重写SQLAlchemyJobStore版】:django中使用apscheduler,使用mysql做存储后端

一、环境配置 python3.8.10 包&#xff1a; APScheduler3.10.4 Django3.2.7 djangorestframework3.15.1 SQLAlchemy2.0.29 PyMySQL1.1.0 项目目录情况 gs_scheduler 应用 commands &#xff1a; 主要用来自定义命令&#xff0c;python manage.py crontab schedulers&#…

20240509解决Protel99se导入philips.ddb出现File is not recognized的问题

20240509解决Protel99se导入philips.ddb出现File is not recognized的问题 2024/5/9 16:25 缘起&#xff1a;最近需要用到/画PCB&#xff0c;想到十年前用过Protel99SE。 使用的系统&#xff1a;WIN10/WIN11都会出错。WIN7没有测试&#xff01; 从115网盘的角落里找到七集视频…

解放摄影潜能,Topaz Photo AI for Mac/Win 引领降噪新潮流

Topaz Photo AI for Mac/Win 是一款领先的人工智能降噪软件&#xff0c;旨在帮助摄影师和普通用户轻松有效地提高照片质量。随着数字摄影技术的发展&#xff0c;摄影爱好者们不断追求更高质量的图像&#xff0c;而Topaz Photo AI的出现为他们打开了全新的图像处理之门。 先进的…

C++ | Leetcode C++题解之第78题子集

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> t;vector<vector<int>> ans;void dfs(int cur, vector<int>& nums) {if (cur nums.size()) {ans.push_back(t);return;}t.push_back(nums[cur]);dfs(cur 1, nums);t.po…

Linux -- > vim

vi和vim是什么 vi和vim是两款流行的文本编辑器&#xff0c;广泛用于Unix和类Unix系统中。它们以其强大的功能和灵活的编辑能力而闻名&#xff0c;特别是在编程和系统管理中非常受欢迎。 vi&#xff08;Visual Interface&#xff09; vi是最初的文本编辑器之一&#xff0c;由…

【Java从入门到精通】Java 异常处理

在 Java 中&#xff0c;异常处理是一种重要的编程概念&#xff0c;用于处理程序执行过程中可能出现的错误或异常情况。 异常是程序中的一些错误&#xff0c;但并不是所有的错误都是异常&#xff0c;并且错误有时候是可以避免的。 比如说&#xff0c;你的代码少了一个分号&…

idea 使用 git

可以看见项目地址&#xff0c; git clone 地址 就可以拉新项目了 命令 git remote -v

【Unity】Unity项目转抖音小游戏(一) 项目转换

UnityWEBGL转抖音小游戏流程 业务需求&#xff0c;开始接触一下抖音小游戏相关的内容&#xff0c;开发过程中记录一下流程。 相关参考&#xff1a; 抖音文档&#xff1a;https://developer.open-douyin.com/docs/resource/zh-CN/mini-game/develop/guide/game-engine/rd-to-SC…

【Android】Kotlin学习之数据容器(数组for循环遍历)

数组遍历 1. for ( item in arr){…} 2. for ( i in arr.indeces ) {…} (遍历下标) 3. for ((index, item) in arr.withInfex()) {…} (遍历下标和元素) 4. arr.forEach {} ( 遍历元素 ) 5. arr.forEachIndexed{index, item -> …}

2024数维杯数学建模选题建议及各题思路来啦!

大家好呀&#xff0c;2024数维杯数学建模挑战赛开始了&#xff0c;来说一下初步的选题建议吧&#xff1a; 首先定下主基调&#xff0c; 本次数维杯建议选B。难度上C&#xff1e;A&#xff1e;B。B题目是比较经典的数据分析类题目&#xff0c;主要做统计分析差异显著性以及相关…

Qt 6.7功能介绍

Qt 6.7为我们所有喜欢在构建现代应用程序和用户体验时获得乐趣的人提供了许多大大小小的改进。一些新增内容作为技术预览发布&#xff0c;接下来我们就一起来看看吧&#xff1a; 将C20与Qt一起使用 对于许多编译器工具链来说&#xff0c;C20仍然是可选的和实验性的&#xff0c;…

【Git实战】如何将本地仓库推送至Github(windows版)?

最近使用Go语言开发项目&#xff0c;想寻找位操作相关的工具包。找了一圈没有合适的&#xff0c;因此自己写了一个。又想将其推送到Github上&#xff0c;中间逢山开路&#xff0c;遇水搭桥&#xff0c;终于成功将本地Git仓库和Github进行了关联。现将我的方法公之于众&#xff…

如何在JavaScript/Vue中获取当前时间并格式化输出(精确到时分秒)

如何在JavaScript/Vue中获取当前时间并格式化输出&#xff08;精确到时分秒&#xff09; 不只是树&#xff0c;人也是一样&#xff0c;在不确定中生活的人&#xff0c;能比较经得起生活的考验&#xff0c;会锻炼出一颗独立自主的心。在不确定中&#xff0c;就能学会把很少的养分…

二、使用插件一键安装HybirdCLR

预告 本专栏将介绍如何使用这个支持热更的AR开发插件&#xff0c;快速地开发AR应用。 插件简介 通过热更技术实现动态地加载AR场景&#xff0c;简化了AR开发流程&#xff0c;让用户可更多地关注Unity场景内容的制作。 热更方案 基于HybirdCLR HybridCLR是一个特性完整、零成…

数据结构之栈的超详细讲解

目录 引言 一.栈的概念 二.栈的结构 三.栈的实现 栈结构的实现 栈操作函数的声明 栈中方法的实现 栈的初始化 栈的销毁 入栈 出栈 取栈顶元素 判断栈中是否为空 获取栈中数据个数 四.测试 代码展示: 结构展示: 五.小结 六.完整代码 Stack.h Stack.c text…

安卓手机平板使用Termux+Hexo搭建本地博客站点并实现无公网IP远程访问

文章目录 前言1.安装 Hexo2.安装cpolar3.远程访问4.固定公网地址 前言 本文主要介绍如何在安卓手机平板Termux中安装个人hexo博客并结合cpolar内网穿透工具&#xff0c;实现远程访问本地搭建的博客站点&#xff0c;无需公网IP。 Hexo 是一个用 Nodejs 编写的快速、简洁且高效…

STM32的ADC详解

ADC即模拟数字转换器&#xff0c;通常用于将外部的模拟量信号转换为数字信号。STM32的ADC是12位逐次逼近型的模拟数字转换器&#xff0c;最大可以计数到4095&#xff0c;有18个通道&#xff0c;16个外部通道和2个内部通道。 ADC框图 ADC的功能框图可以分为七个部分&#xff1a…

6个让你的活动策划成倍回报的策略-华媒舍

活动策划是一个集思广策、全方位考虑的过程&#xff0c;只有通过科学合理的策略规划&#xff0c;才能在有限的资源下取得最大的回报。本文将分享六个让你的活动策划成倍回报的策略&#xff0c;包括目标设定、策划团队、预算控制、宣传推广、参与体验和后期回顾。 1. 目标设定 …

基于LMV358的负电源架构

嘿UU们&#xff0c;中午好啊&#xff01;吃了没&#xff1f;算算时间我的餐桌上应该快上杨梅和鱼胶冻了。 今天看某群&#xff0c;突然想到Jim williams的书里一个架构&#xff0c;但老爷子的东西是正负输出的&#xff0c;而且略微有点麻烦&#xff0c;我就想怎么样整个更适合…