当前位置: 首页 > 编程日记 > 正文

OC学习篇之---对象的拷贝

在前一篇文章中我们说到了如何解决对象的循环引用问题:http://blog.csdn.net/jiangwei0910410003/article/details/41926369,这一篇文章我们就来介绍一下OC中的对象拷贝概念,这个对于面向对象语言中都会有这种的问题,只是不同的语言有不同的解决方式:C++中有拷贝构造函数,Java中需要实现Cloneable接口,在clone方法中进行操作。但是不过OC更偏向于Java这种方式,OC中如果一个对象需要被拷贝,他需要实现协议:

<NSCopying><NSMutableCopying>

从名字上我们可以看到,一个协议是用于不可变对象的,一个协议适用于可变对象的


首先来介绍一下对象的拷贝的概念吧:

为什么要由对象的拷贝这么一个概念呢?看一个场景:假如现在一个对象中又一个数组对象,现在我们生成一个对象,同时将这个对象赋值给另外一个对象,那么现在问题是这两个对象中的数组对象是同一个,那么如果一个对象中去修改这个数值中的内容,另外一个对象中的数组内容也会被修改,相当于这个数组对象是共享的,当然我们有时候是不希望这种形式的出现的,这时候我们就出现了对象的拷贝。

具体来看一个例子吧


一、系统类对象的拷贝

//
//  main.m
//  30_CopyObject
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//#import <Foundation/Foundation.h>/***/
int main(int argc, const char * argv[]) {@autoreleasepool {//对象具备拷贝功能,必须实现如下协议//<NSCopying>、<NSMutableCopying>//copy方法返回的是一个不可变对象,mutableCopy方法返回的是一个可变对象/*NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"one",@"two",nil];NSMutableArray *array2 = [array1 retain];//retain只是引用计数+1,没有创建新的对象//array1与array2指针相同,指向同一个对象if(array1 == array2){NSLog(@"array1 == array2");NSLog(@"array1的引用计数:%ld",array1.retainCount);}*/NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"one",@"two",nil];//复制对象,创建一个新的副本对象//这里使用copy方法复制,返回的是一个不可变数组,但是用一个可变数组来声明,但是我们关心的是指针的的内容,而不是类型//所以array2的真实类型还是不可变类型的NSMutableArray *array2 = [array1 copy];//array2计数为:1,因为是新创建出来的对象//使用mutableCopy方法,返回的就是可变数组//当然这种方法只针对于那些有可变对象之分有用,对于其他的对象这个方法和copy方法的效果是一样的NSMutableArray *array3 = [array1 mutableCopy];if(array1 != array2){NSLog(@"array1 != array2");NSLog(@"array1的引用计数:%ld",array1.retainCount);NSLog(@"array2的引用计数:%ld",array2.retainCount);}[array2 release];[array1 release];}return 0;
}
我们看到,NSMutableArray有一个mutableCopy方法,这样返回的一个数组对象就是一个拷贝对象了。

但是这里需要注意的是:

copy方法和mutableCopy方法的区别

这两个方法的区别只在于那些有可变对象和不可变对象之分的对象上,对于没有这种区分的对象来说,这两个方法的效果是一样的。

[不可变对象 copy]是假拷贝,等价于[不可变对象 retain]

[不可变对象 mutableCopy是真拷贝



二、深拷贝和浅拷贝

在拷贝对象中也是有深拷贝和浅拷贝之分的

浅拷贝:只拷贝所有属性对象的指针

深拷贝:拷贝属性对象的内容

看个例子:

Person.h

//
//  Person.h
//  31_DeepCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//#import <Foundation/Foundation.h>@interface Person : NSObject <NSCopying>@property(nonatomic,retain)NSMutableArray *apples;
@property(nonatomic)int age;@end


Person.m

//
//  Person.m
//  31_DeepCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//#import "Person.h"@implementation Person- (id)copyWithZone:(NSZone *)zone{//创建一个新的副本对象//这个方法是会被继承的,所以这里还是不用//[Person allocWithZone:<#(struct _NSZone *)#>];Person * p = [[self class] allocWithZone:zone];//p.apples = _apples;//是指针赋值,所以还是浅拷贝//深拷贝//拷贝之后引用计数会+1,需要release以下p.apples = [_apples mutableCopy];p.age = _age;[p.apples release];//但是如果我们使用->语法就不需要了,因为我们没有使用set方法,引用计数没有操作//但是这种方式我们不采用//p->_apples = [_apples mutableCopy];return p;
}@end

我们看到,Person实现了NSCopying协议,然后需要实现一个方法:copyWithZone

在这个方法中我们开始进行拷贝操作:

Person类中有一个属性类型是数组

这里我们需要生成一个Person对象,然后进行属性的拷贝,最后在返回这个对象

浅拷贝:直接复制数组指针

深拷贝:直接复制数组的内容,这里可以直接使用mutableCopy方法进行实现


测试类

main.m

//
//  main.m
//  31_DeepCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//#import <Foundation/Foundation.h>
#import "Person.h"//深拷贝和浅拷贝
//默认是浅拷贝
int main(int argc, const char * argv[]) {@autoreleasepool {NSMutableArray *array1 = [NSMutableArray arrayWithCapacity:2];for(int i=0;i<2;i++){Person *p = [[Person alloc] init];[array1 addObject:p];[p release];}//引用计数都是1for(Person *p in array1){NSLog(@"复制之前的引用计数:%ld",p.retainCount);NSLog(@"复制之前的指针:%p",p);}//引用计数都是2,因为是浅拷贝,又有指针指向对象了,array2也是使用了person//浅拷贝:只拷贝对象指针//深拷贝:复制属性NSArray *array2 = [array1 copy];for(Person *p in array2){NSLog(@"复制之前的引用计数:%ld",p.retainCount);NSLog(@"复制之前的指针:%p",p);}//这里Person中有一个属性是NSMutableArray,但是我们只是赋值,并不是拷贝//所以这里还不算是深拷贝Person *p = [[Person alloc] init];p.apples = [NSMutableArray arrayWithObjects:@"iphone",@"ipad", nil];p.age = 20;Person *p1 = [p copy];if(p != p1){NSLog(@"p1.age=%d",p1.age);NSLog(@"p1.apples=%@",p1.apples);}}return 0;
}


三、字符串的拷贝

//
//  main.m
//  32_NSStringCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//#import <Foundation/Foundation.h>#import "Person.h"//字符串为什么使用copy
int main(int argc, const char * argv[]) {@autoreleasepool {Person *p = [[Person alloc] init];NSMutableString *name = [NSMutableString stringWithString:@"jack"];p.name = name;//人的名字被修改了//如果Person的name是retain,则此处的name和person对象的name执行的是同一个字符串对象//此处的name修改之后,会导致person的name也被修改,破坏了person对象的封装性//正常情况下,我们会使用set方法设置名字//所以如果使用的是copy的话,就不会修改名字了[name appendString:@"-tom"];//Foundation框架中可复制的对象,当我们拷贝的是一个不可变对象时候//他的作用相当于retain(系统做的内存优化)//所以这里的如果换成NSString类型的时候,其实没有拷贝的动作的,因为NSString是不可变的//但是使用mutableCopy就可以做到拷贝了,mutableCopy是真正意义上的拷贝//mutableCopy拷贝方法,不管什么对象都是真实拷贝//[不可变对象 copy]是假拷贝,等价于[不可变对象 retain]//[不可变对象 mutableCopy是真拷贝}return 0;
}
这里为什么要单独说一下字符串的拷贝呢?

因为字符串是一个特殊的对象,我们应该调用他的copy方法。因为我们对于字符串其实我们是期望他只有一分值得,就看上面的例子:

我们用NSMutableString产生一个name,然后将其赋值给person对象,当我们在外面修改name的内容的时候,其实person的name属性的值也应该修改。所以我们一般在拷贝字符串对象的时候,都会调用他的copy方法


总结

这一篇文章主要介绍了OC中对象拷贝的相关概念和知识点。我们在操作对象的时候,有时候进行拷贝,还要仔细考虑一下是深拷贝还是浅拷贝。




转载于:https://www.cnblogs.com/roccheung/p/5797287.html

相关文章:

linux脚本自定义赋值,JMeter——运用BeanShell给自定义的变量动态赋值

这个计划中定义了以下组件&#xff1a;用户自定义变量&#xff1a;里面定义了2个变量&#xff0c;一个是us(注意&#xff0c;未对它初始化)&#xff0c;一个变量名是&#xff1a;test &#xff0c;值为&#xff1a;123456一个BeanShell Sampler:${test}表示传入BeanShell Sampl…

VS2010与Qt设置出现红线提示

任何类库导入都有可能出现红线&#xff0c;主要原因是“包含目录没有设置”点击“项目”-> &#xff08;你的项目&#xff09;属性->配置属性->VC目录->包含目录 把你的目录写入&#xff08;我的是&#xff1a;H:\Qt\4.8.5\include&#xff09;点击确定~ 完成。转载…

java-方法。(新手)

//导入包。import java.util.Arrays;import java.util.Calendar;import java.util.Date;//创建的一个类。public class zylx1 { //公共静态的主方法。 public static void main(String[] args){ //调用方法。 lx8(); } //创建方法。//使日历向后偏移…

《Bigtable:a distributed storage system for struct data》笔记

Bigtable 可以说是log-structed以及page-oriented 两种存储引擎的整合体&#xff0c;我们性能优越的单机存储引擎leveldb和rocksdb就是基于google的bigtable存储系统实现的。 论文地址Bigtable: A Distributed Storage System for Structured Data bigtable是基于分布式的存储…

linux 查看线程详细信息,Linux 下查看线程信息

1. 使用 pstree -p PIDps aux | grep firefox | grep -v grepcharles 26058 0.0 0.0 4908 1152 ? S 19:17 0:00 /bin/sh /usr/lib/firefox-3.5.4/run-mozilla.sh /usr/lib/firefox-3.5.4/firefoxcharles 26073 7.6 3.4 284264 70164 ? Sl 19:1…

Bootstrap学习的点点滴滴

1&#xff09;网站&#xff1a;http://www.bootcss.com/http://v3.bootcss.com/getting-started/#downloadhttps://github.com/twbs/bootstraphttp://getbootstrap.com杭州移动用的是2.3版本&#xff0c;手册地址&#xff1a; http://docs.bootcss.com/bootstrap-2.3.2/docs/ja…

Angry Grammar Nazi

Angry Grammar Nazi 1、给出的几个要处理的字符串没有重复性&#xff08;子串&#xff09;&#xff0c;不必考虑某个单词是算在哪个种类中&#xff1b; 2、给出的几个要处理的字符串&#xff08;除了“lol”&#xff09;都是单词或短语&#xff0c;可以直接搜索 3、给出的处理长…

Linux——查看

查看当前系统版本&#xff1a; lsb_release -a #没有装&#xff1a;yum install lsb 查看当前运行端口&#xff1a; netstat -atunlp #没有装&#xff1a;yum install net-tools -a &#xff1a;all&#xff0c;表示列出所有的连接&#xff0c;服务监听&#xff0c;Socket…

Rocksdb 事务(一): 隔离性的实现

文章目录前言1. 隔离性2. Rocksdb实现的隔离级别2.1 常见的四种隔离级别2.2 Rocksdb 支持的隔离级别及基本实现2.2.1 ReadComitted 隔离级别的测试2.2.2 ReadCommitted的实现2.2.3 RepeatableRead的实现2.2.4 事务并发处理3. 一些总结前言 Rocksdb 作为单机存储引擎&#xff0…

linux镜像修改密码,OpenStack 镜像修改密码方案

现在各大linux厂商&#xff0c;其实已经有专门给openStack提供的镜像&#xff0c;不过国内的朋友&#xff0c;不太习惯老外做镜像的方式&#xff0c;经常问密码是多少。本博客提供几种修改密码方案&#xff0c;仅供参考。前言对OpenStack云主机来说&#xff0c;有以下几种方式登…

专题8-Linux系统调用

1.从用户空间到内核空间的时候需要进行系统调用 2.系统调用通过swi指令实现&#xff0c;这个时候会有一个参数&#xff0c;存放到r7寄存器&#xff0c;用来指定要调用的内核态函数。 3.一般内核态的实际函数可以再任意的.c文件实现&#xff0c;但是必须在call.S里面加上相应的函…

企业架构研究总结(39)——TOGAF架构能力框架之架构委员会和架构合规性

3. 架构委员会 正如前面所说&#xff0c;一个用来对架构治理策略的实现进行监督的跨组织的架构委员会是架构治理策略成功的主要要素之一。架构委员会应该能够代表所有主要干系人的需求&#xff0c;并且通常还需要对整个架构的审查及维护活动负有高级行政职责。通常来讲&#xf…

linux c编程之fcntl

fcntl可实现对指定文件描述符的各种操作&#xff0c;其函数原型如下&#xff1a; int fcntl(int fd, int cmd, ... /* arg */ ); 其中&#xff0c;操作类型由cmd决定。cmd可取如下值&#xff1a; F_DUPFD&#xff1a;复制文件描述符F_DUPFD_CLOEXEC&#xff1a;复制文件描述符&…

Rocksdb iterator和snapshot 接口

Rocksdb提供迭代器来来访问整个db中的数据&#xff0c;就像STL中的迭代器功能一样&#xff0c;用来访问容器中的具体的数据。 访问形式以及访问接口有如下几种&#xff1a; 遍历所有的key-value//打开db&#xff0c;并初始化一个迭代器指针 rocksdb::Iterator* it db->Ne…

原生js自动完成 [转]

本来想用jquery的autocomplete的,可是需求有些变化,打算改源码,进了源码发现,改起来要的时间太长了,毕竟不是自己写的,改起来慢,在网上开始大肆搜罗资料,终于找到了类似的 本文转自http://www.cnblogs.com/jaiho/archive/2011/02/28/js_autocomplete.html 完成有以下功能&#…

linux 内存管理slab源码,Linux内核源代码情景分析-内存管理之slab-回收

图 1我们看到空闲slab块占用的若干页面&#xff0c;不会自己释放&#xff1b;我们是通过kmem_cache_reap和kmem_cache_shrink来回收的。他们的区别是&#xff1a;1、我们先看kmem_cache_shrink&#xff0c;代码如下&#xff1a;int kmem_cache_shrink(kmem_cache_t *cachep){if…

Vlookup的兄弟lookup讲解

Vlookup是查找函数&#xff0c;lookup也是&#xff0c;但它主要是充当模糊查找。最常见的例子就是算个税等级和成绩区间。我们创建源数据如图结果要求的是300&#xff0c;500&#xff0c;50对应的积分情况。因为数据量大&#xff0c;这里只取小部分。运用lookup函数&#xff0c…

6.python探测Web服务质量方法之pycurl模块

才开始学习的时候有点忽略了这个模块&#xff0c;觉得既然Python3提供了requests库&#xff0c;为什么多此一举学习这个模块。后来才发现pycurl在探测Web服务器的时候的强大。 pycurl是一个用c语言写的libcurl Python实现&#xff0c;支持的操作协议有FTP&#xff0c;HTTP&…

Rocksdb DeleteRange实现原理

文章目录1. 基本介绍2. 两种接口使用及简单性能对比3. DeleteRange 的基本实现3.1 写流程的实现3.2 读流程的实现 -- skyline算法以下涉及到的代码都是基于rocksdb 6.4.0版本进行描述的 1. 基本介绍 DeleteRange接口的设计是为了代替传统的删除一个区间[start,end) 内的key-va…

题目1460:Oil Deposit

题目描述&#xff1a;The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides the land into numerous square plots. I…

linux做预警机制,预警通告:Linux内核中TCP SACK机制远程DoS

漏洞描述2019年6月18日&#xff0c;RedHat官网发布报告&#xff1a;安全研究人员在Linux内核处理TCPSACK数据包模块中发现了三个漏洞&#xff0c;CVE编号为CVE-2019-11477、CVE-2019-11478和CVE-2019-11479&#xff0c;其中CVE-2019-11477漏洞能够降低系统运行效率&#xff0c;…

C# 使用xsd文件验证XML 格式是否正确

//创建xmlDocument XmlDocument doc new XmlDocument(); //创建声明段 如<?xml version"1.0" encoding"utf-8" ?> doc.AppendChild(doc.CreateXmlDeclaration("1.0", "utf-8", null)); //创建一个根节点 KYTResults Xm…

[蓝桥杯]PREV-23.历届试题_数字游戏

问题描述栋栋正在和同学们玩一个数字游戏。游戏的规则是这样的&#xff1a;栋栋和同学们一共n个人围坐在一圈。栋栋首先说出数字1。接下来&#xff0c;坐在栋栋左手边的同学要说下一个数字2。再下面的一个同学要从上一个同学说的数字往下数两个数说出来&#xff0c;也就是说4。…

Mac 上使用 Clion 阅读C++源码的一些操作

一直在尝试一些写代码方便&#xff0c;阅读代码也很方便的工具&#xff0c;因为使用的是Mac&#xff0c;所以阅读源码上面sourceInsight就没办法用了。 从vscode – sublime – clion 想要可配置性强一点&#xff0c;软件轻一点&#xff0c;也能提供足够的便捷操作&#xff0c…

c语言 字母 八进制表示'/1011',C语言C语言第一课:C语言概述为什么学习C语言怎样学习C语言.DOC...

[摘要]C语言 第一课&#xff1a; C语言概述 为什么学习C语言 怎样学习C语言 参考资料 ----------------------------------------------------------- 入门经典《C语言程序设计》 谭浩强 清华 《汇编语言》 王爽 《The C programming language》 机械工业 《C Primer Plus》 60…

Discuz! X2.5 添加自定义数据调用模块(简单方法)

转&#xff1a;http://521-wf.com/archives/46.html Discuz! X2.5 添加自定义数据调用模块&#xff08;简单方法&#xff09; Discuz!X系列的diy功能还是相当不错的&#xff0c;在对其进行二次开发的过程中&#xff0c;或许需要加入新的数据调用模块&#xff0c;这样可以使你开…

Cassandra数据模型设计最佳实践

2019独角兽企业重金招聘Python工程师标准>>> 本文是Cassandra数据模型设计第一篇&#xff08;全两篇&#xff09;&#xff0c;该系列文章包含了eBay使用Cassandra数据模型设计的一些实践。其中一些最佳实践我们是通过社区学到的&#xff0c;有些对我们来说也是新知识…

矩阵相关概念的物理意义

参考链接&#xff1a; 矩阵乘法的本质是什么&#xff1f; 条件数 病态矩阵与条件数&#xff08;&& 与特征值和SVD的关系&#xff09;矩阵的物理意义&#xff1a;https://blog.csdn.net/NightkidLi_911/article/details/38178533https://blog.csdn.net/NightkidLi_911/a…

Linux 下 进程运行时内部函数耗时的统计 工具:pstack,strace,perf trace,systemtap

简单记录一些 在linux下 统计进程内部函数运行耗时的统计工具&#xff0c;主要是用作性能瓶颈分析。当然以下工具除了pstack功能单一之外&#xff0c;其他的工具都非常强大&#xff0c;这里仅仅整理特定场景的特定用法&#xff0c;用作协同分析。 以下工具需要追踪具体的进程&…

c语言作业扩展名通常为什么,C语言的源程序通常的扩展名是( )

C语言的源程序通常的扩展名是( )更多相关问题【C20】A&#xff0e;asB&#xff0e;afterC&#xff0e;untilD&#xff0e;whenAlthough I spoke to her about the matter several times, she took little ______ of what I s“以质取胜”战略包括三个方面内容&#xff0c;分别是…