OC中对象拷贝概念
<NSCopying>、<NSMutableCopying>
从名字上我们可以看到,一个协议是用于不可变对象的,一个协议适用于可变对象的
首先来介绍一下对象的拷贝的概念吧:
为什么要由对象的拷贝这么一个概念呢?看一个场景:假如现在一个对象中又一个数组对象,现在我们生成一个对象,同时将这个对象赋值给另外一个对象,那么现在问题是这两个对象中的数组对象是同一个,那么如果一个对象中去修改这个数值中的内容,另外一个对象中的数组内容也会被修改,相当于这个数组对象是共享的,当然我们有时候是不希望这种形式的出现的,这时候我们就出现了对象的拷贝。
具体来看一个例子吧
一、系统类对象的拷贝
[objc] view plaincopy
1. //
2. // main.m
3. // 30_CopyObject
4. //
5. // Created by jiangwei on 14-10-13.
6. // Copyright (c) 2014年 jiangwei. All rights reserved.
7. //
8.
9. #import <Foundation/Foundation.h>
10.
11. /**
12.
13. */
14. int main(int argc, const charchar * argv[]) {
15. @autoreleasepool {
16.
17. //对象具备拷贝功能,必须实现如下协议
18. //<NSCopying>、<NSMutableCopying>
19.
20. //copy方法返回的是一个不可变对象,mutableCopy方法返回的是一个可变对象
21.
22. /*
23. NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"one",@"two",nil];
24. NSMutableArray *array2 = [array1 retain];
25. //retain只是引用计数+1,没有创建新的对象
26. //array1与array2指针相同,指向同一个对象
27. if(array1 == array2){
28. NSLog(@"array1 == array2");
29. NSLog(@"array1的引用计数:%ld",array1.retainCount);
30. }
31. */
32.
33. NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"one",@"two",nil];
34. //复制对象,创建一个新的副本对象
35. //这里使用copy方法复制,返回的是一个不可变数组,但是用一个可变数组来声明,但是我们关心的是指针的的内容,而不是类型
36. //所以array2的真实类型还是不可变类型的
37. NSMutableArray *array2 = [array1 copy];//array2计数为:1,因为是新创建出来的对象
38.
39. //使用mutableCopy方法,返回的就是可变数组
40. //当然这种方法只针对于那些有可变对象之分有用,对于其他的对象这个方法和copy方法的效果是一样的
41. NSMutableArray *array3 = [array1 mutableCopy];
42.
43. if(array1 != array2){
44. NSLog(@"array1 != array2");
45. NSLog(@"array1的引用计数:%ld",array1.retainCount);
46. NSLog(@"array2的引用计数:%ld",array2.retainCount);
47. }
48. [array2 release];
49. [array1 release];
50.
51.
52. }
53. return 0;
54. }
我们看到,NSMutableArray有一个mutableCopy方法,这样返回的一个数组对象就是一个拷贝对象了。
但是这里需要注意的是:
copy方法和mutableCopy方法的区别
这两个方法的区别只在于那些有可变对象和不可变对象之分的对象上,对于没有这种区分的对象来说,这两个方法的效果是一样的。
[不可变对象 copy]是假拷贝,等价于[不可变对象 retain]
[不可变对象 mutableCopy是真拷贝
二、深拷贝和浅拷贝
在拷贝对象中也是有深拷贝和浅拷贝之分的
浅拷贝:只拷贝所有属性对象的指针
深拷贝:拷贝属性对象的内容
看个例子:
Person.h
[objc] view plaincopy
1. //
2. // Person.h
3. // 31_DeepCopy
4. //
5. // Created by jiangwei on 14-10-13.
6. // Copyright (c) 2014年 jiangwei. All rights reserved.
7. //
8.
9. #import <Foundation/Foundation.h>
10.
11. @interface Person : NSObject <NSCopying>
12.
13. @property(nonatomic,retain)NSMutableArray *apples;
14. @property(nonatomic)int age;
15.
16. @end
Person.m
[objc] view plaincopy
1. //
2. // Person.m
3. // 31_DeepCopy
4. //
5. // Created by jiangwei on 14-10-13.
6. // Copyright (c) 2014年 jiangwei. All rights reserved.
7. //
8.
9. #import "Person.h"
10.
11. @implementation Person
12.
13. - (id)copyWithZone:(NSZone *)zone{
14. //创建一个新的副本对象
15. //这个方法是会被继承的,所以这里还是不用
16. //[Person allocWithZone:<#(struct _NSZone *)#>];
17. Person * p = [[self class] allocWithZone:zone];
18. //p.apples = _apples;//是指针赋值,所以还是浅拷贝
19. //深拷贝
20. //拷贝之后引用计数会+1,需要release以下
21. p.apples = [_apples mutableCopy];
22. p.age = _age;
23.
24. [p.apples release];
25.
26. //但是如果我们使用->语法就不需要了,因为我们没有使用set方法,引用计数没有操作
27. //但是这种方式我们不采用
28. //p->_apples = [_apples mutableCopy];
29.
30. return p;
31. }
32.
33. @end
我们看到,Person实现了NSCopying协议,然后需要实现一个方法:copyWithZone
在这个方法中我们开始进行拷贝操作:
Person类中有一个属性类型是数组
这里我们需要生成一个Person对象,然后进行属性的拷贝,最后在返回这个对象
浅拷贝:直接复制数组指针
深拷贝:直接复制数组的内容,这里可以直接使用mutableCopy方法进行实现
测试类
main.m
[objc] view plaincopy
1. //
2. // main.m
3. // 31_DeepCopy
4. //
5. // Created by jiangwei on 14-10-13.
6. // Copyright (c) 2014年 jiangwei. All rights reserved.
7. //
8.
9. #import <Foundation/Foundation.h>
10. #import "Person.h"
11.
12. //深拷贝和浅拷贝
13. //默认是浅拷贝
14. int main(int argc, const charchar * argv[]) {
15. @autoreleasepool {
16.
17. NSMutableArray *array1 = [NSMutableArray arrayWithCapacity:2];
18.
19. for(int i=0;i<2;i++){
20. Person *p = [[Person alloc] init];
21. [array1 addObject:p];
22. [p release];
23. }
24.
25. //引用计数都是1
26. for(Person *p in array1){
27. NSLog(@"复制之前的引用计数:%ld",p.retainCount);
28. NSLog(@"复制之前的指针:%p",p);
29. }
30.
31. //引用计数都是2,因为是浅拷贝,又有指针指向对象了,array2也是使用了person
32. //浅拷贝:只拷贝对象指针
33. //深拷贝:复制属性
34. NSArray *array2 = [array1 copy];
35. for(Person *p in array2){
36. NSLog(@"复制之前的引用计数:%ld",p.retainCount);
37. NSLog(@"复制之前的指针:%p",p);
38. }
39.
40. //这里Person中有一个属性是NSMutableArray,但是我们只是赋值,并不是拷贝
41. //所以这里还不算是深拷贝
42. Person *p = [[Person alloc] init];
43. p.apples = [NSMutableArray arrayWithObjects:@"iphone",@"ipad", nil nil];
44. p.age = 20;
45.
46. Person *p1 = [p copy];
47.
48. if(p != p1){
49. NSLog(@"p1.age=%d",p1.age);
50. NSLog(@"p1.apples=%@",p1.apples);
51. }
52.
53. }
54. return 0;
55. }
三、字符串的拷贝
[objc] view plaincopy
1. //
2. // main.m
3. // 32_NSStringCopy
4. //
5. // Created by jiangwei on 14-10-13.
6. // Copyright (c) 2014年 jiangwei. All rights reserved.
7. //
8.
9. #import <Foundation/Foundation.h>
10.
11. #import "Person.h"
12.
13. //字符串为什么使用copy
14. int main(int argc, const charchar * argv[]) {
15. @autoreleasepool {
16. Person *p = [[Person alloc] init];
17. NSMutableString *name = [NSMutableString stringWithString:@"jack"];
18. p.name = name;
19.
20. //人的名字被修改了
21. //如果Person的name是retain,则此处的name和person对象的name执行的是同一个字符串对象
22. //此处的name修改之后,会导致person的name也被修改,破坏了person对象的封装性
23. //正常情况下,我们会使用set方法设置名字
24. //所以如果使用的是copy的话,就不会修改名字了
25. [name appendString:@"-tom"];
26.
27. //Foundation框架中可复制的对象,当我们拷贝的是一个不可变对象时候
28. //他的作用相当于retain(系统做的内存优化)
29.
30. //所以这里的如果换成NSString类型的时候,其实没有拷贝的动作的,因为NSString是不可变的
31. //但是使用mutableCopy就可以做到拷贝了,mutableCopy是真正意义上的拷贝
32. //mutableCopy拷贝方法,不管什么对象都是真实拷贝
33.
34. //[不可变对象 copy]是假拷贝,等价于[不可变对象 retain]
35. //[不可变对象 mutableCopy是真拷贝
36. }
37. return 0;
38. }
这里为什么要单独说一下字符串的拷贝呢?
因为字符串是一个特殊的对象,我们应该调用他的copy方法。因为我们对于字符串其实我们是期望他只有一分值得,就看上面的例子:
我们用NSMutableString产生一个name,然后将其赋值给person对象,当我们在外面修改name的内容的时候,其实person的name属性的值也应该修改。所以我们一般在拷贝字符串对象的时候,都会调用他的copy方法
总结
OC中对象拷贝概念的更多相关文章
- OC中协议的概念以及用法
OC中协议的概念以及用法,协议也是OC中的一个重点,Foundation框架以及我们后面在写代码都会用到. OC中的协议就是相当于Java中的接口(抽象类),只不过OC中的名字更形象点,因为我们在学习 ...
- [BS-21] 关于OC中对象与指针的思考
关于OC中对象与指针的思考 1. 创建对象: OC中可通过代码Person *p = [[Person alloc] init];创建了一个对象p.该过程中内存情况为: 在当前线程的栈(默认1M)中, ...
- OC中对象元素的引用计数 自动释放池的相关概念
OC中数组对象在是如何处理对象元素的引用计数问题的,同时介绍一下自动释放池的相关概念 一.数组对象是如何处理对象元素的引用计数问题[objc] view plaincopy 1. // 2. / ...
- 编译时和运行时、OC中对象的动态编译机制
编译时 编译时顾名思义就是正在编译的时候.那啥叫编译呢?就是编译器帮你把源代码翻译成机器能识别的代码.(当然只是一般意义上这么说,实际上可能只是翻译成某个中间状态的语言.比如Java只有JVM识别的字 ...
- Java中对象拷贝的两种方式
引用的拷贝 //引用拷贝 private static void copyReferenceObject(){ Person p = new Person(23, "zhang") ...
- 谈谈Python中对象拷贝
你想复制一个对象?因为在Python中,无论你把对象做为参数传递,做为函数返回值,都是引用传递的. 何谓引用传递,我们来看一个C++交换两个数的函数: void swap(int &a, in ...
- oc中对象的初始化
在.m文件中使用对象方法: - (id)init { _name =@"zhangsan"; _age = 18; return self; } 然后通过main方法中进行创建对象 ...
- OC中对象的description方法
周所周知,我们在做项目时, 可以在类的.m文件中重写该类的对象的描述description方法: 示例: -(NSString *)description{ NSString *str = [N ...
- OC中的一个特性:延展
OC中的一个特性:延展其实说白了,延展就是弥补C语言中的前向申明,我们知道,在C语言中,如果你想调用一个函数的话,那么在此之前必须要声明一个这个函数,就是有前置性.OC中为了弥补C语言中的这个问题,就 ...
随机推荐
- 学用 ASP.Net 之 System.Math 类
本文来自:http://www.cnblogs.com/del/archive/2011/01/03/1924746.html 成员: /* 字段 */ Math.E; //2.7182 ...
- C#整理1——进制转换
进制转换:二进制,八进制,十进制,十六进制. (一)二进制转十进制: 1.写2 2.标指数,从右向左,从0开始依次标记 3.乘系数,一一对应. 4.相加. 例:二进制数1101转十进制数* 1.2 ...
- < meta > 元素(转)
< meta > 元素 概要 标签提供关于HTML文档的元数据.元数据不会显示在页面上,但是对于机器是可读的.它可用于浏览器(如何显示内容或重新加载页面),搜索引擎(关键词),或其他 we ...
- css系列教程--选择器
css派生选择器:是指在某个样式表或者dom元素的行内定义行内元素的属性,而其他同名样式在其他dom节点无效的样式表定义方式.例如:div ul li{border:1px solid red;}&l ...
- HTML——框架
1.frameset <html> <frameset cols="25%,50%,25%"> <frame src="frame_a.ht ...
- SQL性能优化的思路建议
如何在 Oracle数据库里写出高质量的SQL语句,如何在Oracle数据库里对有性能问题的SQL做诊断和调整,这是DBA们在ORACLE数据库实践中不可避免的难题.下面就让我们来分析一下拿到一条问题 ...
- 【Android】Fragment如何获取子Fragment
今天搞了个嵌套的Fragment,通过外部的Fragment获取的子Fragment代码: this.navigationBar = (HXKJCargoNavigationView) getFrag ...
- c# 另存为excel
去网上找了一下 看了一个比较简单的新建excel然后另存为. 要引用Microsoft.Office.Interop.Excel命名空间,如果没有的话 ,百度比我懂. 直接付代码: Microsof ...
- eclipse最有用快捷键整理(转)
eclipse最有用快捷键整理 可以通过菜单栏中Window–>Preferences–>General–>Keys来修改快捷键绑定. 编辑 Ctrl+1 快速修复(最经典的快捷键, ...
- hdu 4709 Herding hdu 2013 热身赛
题意:给出笛卡尔坐标系上 n 个点,n不大于100,求出这些点中能围出的最小面积. 可以肯定的是三个点围成的面积是最小的,然后就暴力枚举,计算任意三点围成的面积.刚开始是求出三边的长,然后求面积,运算 ...