【OC学习-26】对象的浅拷贝和深拷贝——关键在于属性是否可被拷贝

对象的拷贝分为浅拷贝和深拷贝,
浅拷贝就是只拷贝对象,但是属性不拷贝,拷贝出来的对象和原来的对象共用属性,即指向同一个属性地址。
深拷贝则相当于不仅拷贝了一个对象还拷贝了它的属性,即完全是两个东西,只不过内容相同而已。

拷贝用到协议,如果这个类创建对象后,这个对象要被拷贝,那么这个类就需要用到拷贝协议,分两种:<NSCopying>和<NSMutableCopying>相当于一个是拷贝,另一个是拷贝后可修改。

(1)浅拷贝的案例。有一个Person类,它创建一个person1对象后,用person1再拷贝一个person2出来。

//Person类的Person.h文件
#import <Foundation/Foundation.h>
//因为这个类要支持拷贝,所以需要引入拷贝协议,有两种,后一种拷贝后可修改
@interface Person : NSObject<NSCopying,NSMutableCopying>
@property(nonatomic,copy) NSString* name;
@property(nonatomic,retain) NSNumber* age;
@end
//这是Person.m文件
#import "Person.h"
@implementation Person
//这是系统函数,可以直接拷贝过来
- (id)copyWithZone:(NSZone *)zone{
Person *person=[[[self class]allocWithZone:zone]init]; //默认格式
person.name=_name; //浅拷贝就是直接赋值即可
person.age=_age; //浅拷贝就是直接赋值即可
return person;
}
@end
//main.m文件
#import <Foundation/Foundation.h>
#import "Person.h"//记得引入头文件
int main(int argc, const char * argv[])
{
@autoreleasepool {
Person *person1=[[Person alloc]init];
person1.name=@"jack";
person1.age=@18;
Person *person2=[person1 copy];
NSLog(@"%p,%p",person1,person2);//输入两个对象地址,不同
NSLog(@"%p,%p",person1.age,person2.age);//输出两个对象的属性地址,相同
}
return 0;
}

结果:

0x1002036f0,0x100200330 //不同
0x1227,0x1227 //相同

(2)深拷贝的案例。

按道理是只需要把Person.m里面的赋值语句改成下面的样子,就能实现深拷贝:

person.name=[_name copy];
person.age=[_age copy];

但是,因为cocoa优化过了,所以有如下规则:

a:如果是Foundation框架里的不可变对象,就是Array,NSString等创建的对象,直接用copy来拷贝相当于retain,也就是属性还是同一个;

b:如果是用mutableCopy来拷贝,不管是可变还是不可变对象,属性神马的都直接拷贝了一份,即真正意义上得拷贝,它拷贝出来的对象统统都是可变的;

c:如果是可变对象,我们用copy也能实现真正意义上的拷贝,但是拷贝出来的对象是不可变的。

所以,我们拿name实验(因为age没有mutableCopy)实现语句的修改:

person.name=[_name mutableCopy]
person.age=[_age copy];

然后再输出person1和person2的name属性的地址,发现就不同了。

总结:

浅拷贝和深拷贝在实际项目中不常用,可以做一般了解。

OC-深浅复制

 

浅 复 制:在复制操作时,对于被复制的对象的每一层复制都是指针复制。

深 复 制:在复制操作时,对于被复制的对象至少有一层复制是对象复制。

完全复制:在复制操作时,对于被复制的对象的每一层复制都是对象复制。

注:1在复制操作时,对于对象有n层是对象复制,我们可称作n级深复制,此处n应大于等于1。

2对于完全复制如何实现(目前通用的办法是:迭代法和归档),这里后续是否添加视情况而定,

暂时不做讲解。

 3、指针复制俗称指针拷贝,对象复制也俗称内容拷贝。

retain:始终是浅复制。引用计数每次加一。返回对象是否可变与被复制的对象保持一致。

copy:对于可变对象为深复制,引用计数不改变;对于不可变对象是浅复制,

引用计数每次加一。始终返回一个不可变对象。

mutableCopy:始终是深复制,引用计数不改变。始终返回一个可变对象。

不可变对象:值发生改变,其内存首地址随之改变。

   可变对象:无论值是否改变,其内存首地址都不随之改变。

   引用计数:为了让使用者清楚的知道,该对象有多少个拥有者(即有多少个指针指向同一内存地址)。

亲爱的读者朋友,下面是我用于验证的详细代码。对于验证还能得出什么结论,我希望朋友们能自己多多发掘一下。这里只做以上几点总结。对于本文有任何疑问请与我联系,欢迎指出本文不足的地方,谢谢!

#import<Foundation/Foundation.h>

int main (int argc, const char * argv[])

{

@autoreleasepool {

//第一种:非容器类不可变对象

NSString *str1=@"one day";

printf("n初始化赋值引用计数为::::%lu",str1.retainCount);

NSString *strCopy1=[str1 retain];

printf("n继续retain引用计数为:::%lu",str1.retainCount);

NSString *strCopy2=[str1 copy];

printf("n继续copy后引用计数为::::%lu",str1.retainCount);

NSString *strCopy3=[str1 mutableCopy];

printf("n继续mutableCopy后为:::%lun",str1.retainCount);

printf("n非容器类不可变对象n原始地址::::::::::%p",str1);

printf("nretain复制::::::::%p",strCopy1);

printf("ncopy复制::::::::::%p",strCopy2);

printf("nmutableCopy复制:::%p",strCopy3);

//这里说明该类型不存在引用计数的概念

 

// 初始化赋值引用计数为:18446744073709551615

// 继续retain引用计数为:18446744073709551615

// 继续copy后引用计数为:18446744073709551615

// 继续mutableCopy后为:18446744073709551615

//非容器类不可变对象

//原始地址::::::::::0x1000033d0

//retain复制::::::::0x1000033d0//浅复制

//copy复制::::::::::0x1000033d0//浅复制

//mutableCopy复制:::0x10010c420//深复制

printf("n");

//第二种:容器类不可变数组

NSArray *array1= [NSArray arrayWithObjects:@"a",@"b",@"c",@"d",nil];

printf("n初始化赋值引用计数为::::::::::::%lu",array1.retainCount);

NSArray *arrayCopy1 = [array1 retain];

printf("n继续retain后引用计数为:::::::::%lu",array1.retainCount);

NSArray *arrayCopy2 = [array1 copy];

printf("n继续copy后引用计数为:::::::::::%lu",array1.retainCount);

NSArray *arrayCopy3 = [array1 mutableCopy];

printf("n继续mutableCopy后引用计数为::::%lun",array1.retainCount);

printf("n容器类不可变数组n原始地址::::::::::%ptt%p",array1,[array1 objectAtIndex:1]);

printf("nretain复制::::::::%pt%p",arrayCopy1,[arrayCopy1 objectAtIndex:1]);

printf("ncopy复制::::::::::%pt%p",arrayCopy2,[arrayCopy2 objectAtIndex:1]);

printf("nmutableCopy复制:::%pt%p",arrayCopy3,[arrayCopy3 objectAtIndex:1]);

//初始化赋值引用计数为::::::::::::1

//继续retain后引用计数为:::::::::2

//继续copy后引用计数为:::::::::::3

//继续mutableCopy后引用计数为::::3

//容器类不可变数组

//原始地址::::::::::0x10010c6b0 0x100003410

//retain复制::::::::0x10010c6b0 0x100003410//浅复制

//copy复制::::::::::0x10010c6b0 0x100003410//浅复制

//mutableCopy复制:::0x10010c760 0x100003410//深复制

printf("n");

 //第三种:非容器类可变对象

NSMutableString *str2=[NSMutableString stringWithString:@"two day"];

printf("n初始化赋值引用计数为::::::::::::%lu",str2.retainCount);

NSMutableString *strCpy1=[str2 retain];

printf("n继续retain后引用计数为:::::::::%lu",str2.retainCount);

NSMutableString *strCpy2=[str2 copy];

printf("n继续copy后引用计数为:::::::::::%lu",str2.retainCount);

NSMutableString *strCpy3=[str2 mutableCopy];

printf("n继续mutableCopy后引用计数为::::%lun",str2.retainCount);

printf("n非容器类可变对象n原始地址::::::::::%p",str2);

printf("nretin复制::::::::%p",strCpy1);

printf("ncopy复制::::::::::%p",strCpy2);

printf("nmutableCopy复制:::%p",strCpy3);

//初始化赋值引用计数为::::::::::::1

//继续retain后引用计数为:::::::::2

//继续copy后引用计数为:::::::::::2

//继续mutableCopy后引用计数为::::2

//非容器类可变对象

//原始地址::::::::::0x10010c560

//retain复制::::::::0x10010c560//浅复制

//copy复制::::::::::0x100102720//深复制

//mutableCopy复制:::0x10010c880//深复制

printf("n");

//第四种:容器类可变数组

NSMutableArray *array2   = [NSMutableArrayarrayWithObjects:@"aa",@"bb",@"cc",@"dd",nil];

printf("n初始化赋值引用计数为::::::::::%lu",array2.retainCount);

NSMutableArray *arrayCpy1 = [array2 retain];

printf("n继续retain后引用计数为:::::::%lu",array2.retainCount);

NSMutableArray *arrayCpy2=[array2 copy];

printf("n继续copy后引用计数为:::::::::%lu",array2.retainCount);

NSMutableArray *arrayCpy3 = [array2 mutableCopy];

printf("n继续mutableCopy后引用计数为::%lun",array2.retainCount);

printf("n容器类可变数组n原始地址:::::::::::%pt%p",array2,[array2 objectAtIndex:1]);

printf("nretain复制:::::::::%pt%p",arrayCpy1,[arrayCpy1 objectAtIndex:1]);

printf("ncopy复制:::::::::::%pt%p",arrayCpy2,[arrayCpy2 objectAtIndex:1]);

printf("nnmutableCopy复制:::%pt%p",arrayCpy3,[arrayCpy3 objectAtIndex:1]);

//初始化赋值引用计数为::::::::::1

//继续retain后引用计数为:::::::2

//继续copy后引用计数为:::::::::2

//继续mutableCopy后引用计数为::2

//容器类可变数组

//原始地址:::::::::::0x10010e6c0 0x1000034b0

//retain复制:::::::::0x10010e6c0 0x1000034b0//浅复制

//copy复制:::::::::::0x10010e790 0x1000034b0//深复制

//nmutableCopy复制:::0x10010e7c0 0x1000034b0//深复制

}

return 0;

}

OC-深浅复制的更多相关文章

  1. OC深浅复制

    浅复制:指针的复制 深复制:内容的复制 主要有两个关键字  copy 和mutablecopy 对于基本类型 判断深浅方法 1.只要=右边从创建到赋值,至少包含一个NSMutable便会重新生成一个对 ...

  2. 详谈OC(object-c)深浅复制/拷贝-什么情况下用retain和copy

    读前小提示:对于深浅复制有一个清楚的了解,对于学习oc的朋友来说,至关重要.那么首先,我们要明白深浅复制是如何定义的呢.这里为了便于朋友们理解,定义如下. 浅 复 制:在复制操作时,对于被复制的对象的 ...

  3. Objective-c 深浅复制

    深浅复制的定义: 浅复制:在复制时,对于被复制对象的每一层都是指针复制. 深复制:在复制时,对于被复制的对象至少有一层是对象复制. 完全复制:在复制时,对于被复制对象的每一层都是完全复制. retai ...

  4. Python基础之列表深浅复制和列表推导式

    一.列表深浅复制: 浅拷贝内存图如下: 深拷贝内存图如下: 二.列表推导式: 实例: """ 列表推导式 练习:exercise01 """ ...

  5. C# 深浅复制 MemberwiseClone(转载)

    最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考MSDN:https://docs.microsoft.com/zh-cn/dotnet/api/syst ...

  6. C# 深浅复制 MemberwiseClone

    学无止境,精益求精 十年河东,十年河西,莫欺少年穷 学历代表你的过去,能力代表你的现在,学习代表你的将来 最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考 ...

  7. 潭州课堂25班:Ph201805201 第五课:格式化输出和深浅复制 (课堂笔记)

    格式化输出和字符串转义 占位符 使用示意 作用 %s '%s %s' % ('hello', 'world') 表示占位的是str %d '%d %d' % (1, 2) 表示占位的是int %d ' ...

  8. C#设计模式(6)——原型模式(Prototype Pattern) C# 深浅复制 MemberwiseClone

    C#设计模式(6)——原型模式(Prototype Pattern)   一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创 ...

  9. 【Python初级】由判定回文数想到的,关于深浅复制,以及字符串反转的问题

    尝试用Python实现可以说是一个很经典的问题,判断回文数. 让我们再来看看回文数是怎么定义的: 回数是指从左向右读和从右向左读都是一样的数,例如1,121,909,666等 解决这个问题的思路,可以 ...

随机推荐

  1. MyBatis完全使用指南

    MyBatis完全使用指南 转 http://www.jianshu.com/p/1c7c7d1bba33 MyBatis MyBatis作为一个轻量的SQL映射框架,确实很简单,但是知识点挺多,实际 ...

  2. 网络子系统43_ip选项预处理

    //选项格式: // 1.type中指示该选项在分片时是否需要被拷贝 // 2.ptr从1算起,1为type的位置 // 3.len不包括type字段,其余都包括(len,ptr,选项内容) //ty ...

  3. jQuery Animation实现CSS3动画

    jQuery Animation的工作原理是通过将元素的CSS样式从一个状态改变为另一个状态.CSS属性值是逐渐改变的,这样就可以创建动画效果.只有数字值可创建动画(比如 "margin:3 ...

  4. 独立博客怎样申请谷歌Adsense

    谷歌Adsense广告是眼下个人站长的主要赚钱途径之中的一个,首先是它相对诱人的单位价格,尽管谷歌中文广告相比英文广告单位价格有所折扣,但我的经验是仅仅要你的网页内容和广告keyword有较高的匹配性 ...

  5. linux ptrace I

    这几天通过<游戏安全--手游安全技术入门这本书>了解到linux系统中ptrace()这个函数可以实现外挂功能,于是在ubuntu 16.04 x86_64系统上对这个函数进行了学习. 参 ...

  6. Java基础知识强化之集合框架笔记19:List集合迭代器使用之 并发修改异常的产生原因 以及 解决方案

    1. 我有一个集合,如下,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现. ConcurrentModi ...

  7. 本地代码git到github上

    本地代码git到github上 对于个程序员来说,不写自己的博客,没有自己的作品集没有Github就不算好的程序员!咳咳~ 开个玩笑.对于我个人来说,要做个程序员,就要有自己的作品集和技术博客(我说是 ...

  8. Linux试玩指令开机关机

    Linux内核最初只是由芬兰人李纳斯·托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的. Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和U ...

  9. C# 各种集合

    大多数集合都在  System.Collections,System.Collections.Generic两个命名空间. 其中System.Collections.Generic专门用于泛型集合. ...

  10. one way WebService

    WSDL支持4种消息交换方式:   1)单向(One-way):服务端接收消息:   2)请求响应(Request-response):服务端点接收请求消息,然后发送响应消息:   3)要求应答(So ...