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];
} //引用计数都是1
for(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中对象拷贝的相关概念和知识点。我们在操作对象的时候,有时候进行拷贝,还要仔细考虑一下是深拷贝还是浅拷贝。
OC学习篇之---对象的拷贝的更多相关文章
- OC学习篇之---总结和学习目录
今天终于把OC的基础知识学习完了,但是这些知识只是最基础的,还有很多高级知识,这个可能需要后面慢慢的去学习才能体会到.下面就是这次学习OC的目录教程,如果大家发现有什么不正确的地方,请指正,小弟是新生 ...
- OC学习篇之---单例模式
在之前的一片文章中介绍了对象的拷贝相关知识:http://blog.csdn.net/jiangwei0910410003/article/details/41926531,今天我们来看一下OC中的单 ...
- OC学习6——面相对象的三大特性
我们在学习Java的时候都知道,类有三大特性:继承,封装,多态,这也是面向对象的三大特征.OC学习篇之---类的三大特性(封装,继承,多态) 1.封装(Encapsulation)是指将对象的状态信息 ...
- OC学习篇之---数组对象的引用计数问题和自动释放池的概念
之前一片文章中我们介绍了OC中的两个关键字@property和@synthesize的使用的使用: http://blog.csdn.net/jiangwei0910410003/article/de ...
- OC学习篇之---@property和@synthesize的使用
在之前一片文章我们介绍了OC中的内存管理:http://blog.csdn.net/jiangwei0910410003/article/details/41924683,今天我们来介绍两个关键字的使 ...
- (转载)OC学习篇之---归档和解挡
前几篇文章说到了OC中的Foundation框架,今天我们来看一下OC中的一个重要知识点:归档 OC中的归档就是将对象写入到一个文件中,Java中的ObjectInputStream和ObjectOu ...
- (转载)OC学习篇之---概述
前言 终于开启了OC的学习篇了,之前由于工作上的事,学习就一直搁浅了,不过最近由于各种原因,感觉必须要开启iOS的开发旅程了,不然就老了.因为之前一直是做Android的,所以学习iOS来就没那么费劲 ...
- OC学习篇之---归档和解挡
今天我们来看一下OC中的一个重要知识点:归档 OC中的归档就是将对象写入到一个文件中,Java中的ObjectInputStream和ObjectOutputStream来进行操作的.当然在操作的这些 ...
- (转载)OC学习篇之---Foundation框架中的NSString对象和NSMutableString对象
在之前的一篇文章中我们说到了Foundation框架中的NSObject对象,那么今天在在来继续看一下Foundation框架中的常用对象:NSString和NSMutableString. 在OC中 ...
随机推荐
- signer information does not match signer information of other classes in the same package
报错日志: java.lang.SecurityException: class "org.bouncycastle.asn1.ASN1ObjectIdentifier"'s si ...
- Docker(应用服务引擎)
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是完全使用沙箱机制,相互之间不会有任何 ...
- js的几个特殊的运算符略解
js运算符的一些特殊应用及使用技巧. 1. 是否包含指定字符: ~ ~"str1".indexOf("str2") 含义为:str1 被查找的字符串 str2 ...
- django-3-视图(views.py)与网址(urls.py)
视图与网址 操作文件:urls.py.views.py urls.py 作用:用于处理前台的链接(如前台访问:127.0.0.1:8080/index/1212/21212),其实永远访问的是同一个文 ...
- python 装饰器 第一步:基本函数
# 第一步:基本函数 def eat(): print('吃饭') # 调用 eat()
- input只读效果
有两种方式可以实现input的只读效果:disabled 和 readonly. 自然两种出来的效果都是只能读取不能编辑,可是两者有很大不同. Disabled说明该input无效,及其value不会 ...
- 阿里Druid连接池的坑。。
Druid的坑 当查询数据库的Clob转换为Oracle Clob类型的时候. java.lang.ClassCastException: com.alibaba.druid.proxy.jdbc.C ...
- Codeforces 1114E(数学+随机算法)
题面 传送门 分析 通过二分答案,我们显然可以求出数组中最大的数,即等差数列的末项 接着随机取一些数组中的数,对他们两两做差,把得到的差取gcd即为公差 例a={1,5,9,13},我们随机取了1 9 ...
- POJ 2417 Discrete Logging ( Baby step giant step )
Discrete Logging Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 3696 Accepted: 1727 ...
- 在tkinter中使用matplotlib
import sys import tkinter as Tk import matplotlib from numpy import arange, sin, pi from matplotlib. ...