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语言中的这个问题,就 ...
随机推荐
- OC基础1:一些基本概念
"OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1.关于类方法和实例方法: (1).类方法 ...
- MySQL初始化故障-----mysql_config_editor中的坑
今天准备新启一个MySQL实例,结果竟然无法初始化,内容如下: -------------------------------------------------------------------- ...
- 关于js对象值的传递
结合红宝书和网上的一些文章,记录下自己对关于js对象的值的传递的一些理解. js对象是保存在堆内存中的,当把对象赋值给变量时,是把对象在堆内存的引用(地址)赋值给了变量,变量通过地址来访问对象.下面来 ...
- 事件:target与currentTarget区别
target在事件流的目标阶段:currentTarget在事件流的捕获,目标及冒泡阶段.只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的 ...
- sql中的split方法
ALTER function [dbo].[f_split](@SourceSql varchar(8000),@StrSeprate varchar(10))returns @temp table( ...
- C#中in,out,ref,params的作用和区别
ref和out的区别在C# 中,既可以通过值也可以通过引用传递参数.通过引用传递参数允许函数成员更改参数的值,并保持该更改.若要通过引用传递参数, 可使用ref或out关键字.ref和out这两个关键 ...
- beforefieldinit释义(3)
1.看下面的例子: public static class MyClass<T> { public static readonly DateTime Time = GetNow(); pr ...
- 转:一个跨WINDOWS LINUX平台的线程类
来源:http://blog.csdn.net/dengxu11/article/details/7232681 继Windows下实现一个CThread封装类之后,这里我再实现一个跨WINDOWS ...
- 16-GDBT(MART) 迭代决策树入门教程 | 简介
转载:http://blog.csdn.net/w28971023/article/details/8240756 GBDT(Gradient Boosting Decision Tree) 又叫 M ...
- USB OTG学习
1. 概要 OTG设备使用插头中的ID引脚来区分A/B Device,ID接地被称作为A-Device,为连接时候的USB Host,A-Device始终为总线提供电力,ID悬空被称作为B-Devic ...