深拷贝、浅拷贝
来源:互联网 发布:50量子计算机 ibm 知乎 编辑:程序博客网 时间:2024/05/16 18:58
关于深拷贝和浅拷贝的一些笔记
先讲讲写这篇博文的背景吧。
事情发生在一个风和日丽的下午。。。
需求大致如下:
从A页面跳转到B页面,然后跳转时传递了一个NSObject类的数据源(包含数组),在B页面中需要展示数据源中的数据,接着可能修改数据源数组中的某一项或者某几项数据。然后在点保存按钮时回传给A页面,点返回按钮时不改变数据源数据。
BUG描述如下:
1.A页面跳转到B页面,B页面显示的数据正确;
2.B页面修改过某项数据后,点保存按钮后回传给A页面,接着再跳转到B页面,此时数据显示正确;
3.经过2操作后再次修改B中数据,点击返回按钮,回到A页面(此时应该是不需要保存修改的数据),接着再调到B页面,发现B页面显示的是刚才不需要保存的数据。
分析出现BUG的原因
1.在最开始的需求下,我写传递的数据源是一个单利。
2.在数据的传递时,数据源中有数组的存在,在给数组赋值时用了addObjectFromArray这个方法进行数据的拷贝。
写个简单的点的代码意思一下吧。怕自己到时候也不记得自己上面说的是什么。
A.h
#import <UIKit/UIKit.h>@interface HAViewController : UIViewController{ NSArray* m_arrCollcations; NSMutableArray* m_arrSelectedData;}@property (nonatomic,strong) NSArray* propCollcations;@property (nonatomic,strong) NSMutableArray* propSelectedData;@end
A.m
#import "HItemData.h"#import "HProductData.h"-(void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; if (m_arrSelectedData && m_arrSelectedData.count > 0) { HItemData* pItemData = [m_arrCollcations objectAtIndexCheck:m_nIndex]; [pItemData.propProducts removeAllObjects]; [pItemData.propProducts addObjectsFromArray:m_arrSelectedData]; [m_arrSelectedData removeAllObjects]; [m_pTableView reloadData]; }}//跳转方法-(void)ChangeBtnClick:(NSInteger)argIndex{ m_nProductIndex = argIndex; HItemData* pItemData = [[HItemData alloc] init]; HItemData.propItemName = [[m_arrCollcations objectAtIndexCheck:m_n Index] propItemName]; [pItemData.propProducts addObjectsFromArray:[[m_arrCollcations objectAtIndexCheck:m_nIndex] propProducts]]; HBViewController* pBVC = [[HBViewController alloc] init]; pBVC.propPushItemData = pItemData; [self.navigationController pushViewController:pBVC animated:YES];}
B.h
@class HItemData;@interface HBViewController : UIViewController{ HItemData* m_pPushItemData;//传递过来的Data}@property (nonatomic,strong) HItemData * propPushItemData;
B.m
#import "HItemData.h"#import "HProductData.h"@interface HBViewController (){ HItemData* m_pItemData;}@end@implementation HBViewController@synthesize propPushItemData = m_pPushItemData;- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. m_pItemData = [[HItemData alloc] init]; [self PrepareItemData:m_pItemData withOldData:m_pPushItemData]; }- (void)PrepareItemData:(HItemData*)argNewData withOldData:(HItemData*)argOldData{ argNewData.propItemName = argOldData.propItemName; [argNewData.propProducts addObjectsFromArray:argOldData.propProducts];}/** 然后在B.m的文件下写一些改变m_pItemData下的HProductData的属性,实在不愿编了,暂虑吧。*///确认按钮- (void)makeSureButtonClick{ for (HAViewController *temp in self.navigationController.viewControllers) { if ([temp isKindOfClass:[HAViewController class]]) { temp.propSelectedData = m_pItemData.propProducts; [self.navigationController popToViewController:temp animated:YES]; } }}//返回按钮(不保存数据)- (void)backButtonClick{ [self.navigationController popViewControllerAnimated:YES];}
HItemData.h
@interface HItemData : NSObject{ NSString* m_strItemName; NSMutableArray* m_arrProducts;//里面存很多个HProductData}@property (nonatomic, copy ) NSString * propItemName;@property (nonatomic,strong) NSMutableArray* propProducts;
HItemData.m
@implementation HItemData@synthesize propItemName = m_strItemName;@synthesize propProducts = m_arrProducts;- (id)init{ if(self = [super init]) { m_strItemName = @""; m_arrProducts = [[NSMutableArray alloc] init]; } return self;}
HProductData.h
@interface HProductData : NSObject{ NSString* m_strProductName; NSString* m_strProductPrice;}@property (nonatomic, copy ) NSString * propProductName;@property (nonatomic, copy ) NSString * propProductPrice;
HProductData.m
@implementation HProductData@synthesize propProductName = m_strProductName;@synthesize propProductPrice = m_strProductPrice;- (id)init{ if(self = [super init]) { m_strProductName = @""; m_strProductPrice = @""; } return self;}
源代码不太方便传,所以上面的是自己现编的,大致意思就是这样。
然后在实际运行时,就有可能会出现篇头出现的bug,然后就解决呗,想来想去只有可能是addObjectFromArray只是浅拷贝,只拷贝了指针地址,所以在B页面改来改去改的还是同一个东西。
先说说我是怎么解决的吧。
在HProductData.m中
@interface HProductData : NSObject<NSCopying>
在HProductData.m页面里添加下面代码
- (id)copyWithZone:(nullable NSZone *)zone{ HProductData *model = [[[self class] allocWithZone:zone] init]; model.m_strProductName = m_strProductName; model.m_strProductPrice = m_strProductPrice; return model;}
然后将B.m中的PrepareItemData:withOldData:方法改下
- (void)PrepareItemData:(HItemData*)argNewData withOldData:(HItemData*)argOldData{ argNewData.propItemName = argOldData.propItemName; NSArray* arrOldProducts = [[NSArray alloc] initWithArray:argOldData.propProducts copyItems:YES] [argNewData.propProducts addObjectsFromArray:arrOldProducts];}
弱弱的说句,上面的实际运行不知道会不会出现我的bug,因为是现编的,见谅,但是我的解决方法呢,是我上面写的。勿喷!
由此接入一些自己的理解
addObjectsFromArray或者addObject这些数组添加数据的方法会让添加的数据引用计数加一,也就是retain操作(创建一个指针,然后复制指针),相反remove只是引用计数减一
这也就导致我出现BUG的原因,所以我需要内容拷贝。
而内容拷贝就是copy了,copy又有copy和mutableCopy之分。
举个例子
这里可以看到copy完后指向同一块内存区域(weak reference弱引用)
strMutableCopy则是我们所说的真正意义上的复制,系统为其分配了新内存,但指针所指向的字符串还是和string所指的一样。
总结如下:
1.retain:始终是浅复制。引用计数每次加一。返回对象是否可变与被复制的对象保持一致。
2.copy:对于可变对象为深复制,引用计数不改变;对于不可变对象是浅复制, 引用计数每次加一。始终返回一个不可变对象。 (但对于容器对象本身来说是深拷贝。)
3.mutableCopy:始终是深复制,引用计数不改变。始终返回一个可变对象。(对于容器对象本身来说也是深拷贝。)
好吧,一步步往前扒。
因为我的数据源是一个所谓的自定义对象,所以不能直接对我代码中的HProductData进行copy操作,那么我们自己要实现NSCopying,NSMutableCopying这样就能调用copy和mutablecopy了。也就有了HProductData.h类中的修改
接着就有了刚才改BUG时重写的copyWithZone方法了。
但是这里会想,我重写的copyWithZone方法里也没有copy操作或者mutablecopy操作。这是为什么呢。
因为
@property (nonatomic, copy ) NSString * propProductName;
已经替我们完成了。
@property即调用了
- (NSString*) propProductName
{
return propProductName;
}
- (void)setpropProductName:(NSString *)propProductName
{
_propProductName = [propProductName copy];
}
然后我在B.m中写了个initWithArray:copyItems:方法。这里解释下
集合的深复制有两种方法。可以用 initWithArray:copyItems: 将第二个参数设置为YES即可深复制,就如我上面的代码。
如果你用这种方法深复制,集合里的每个对象都会收到 copyWithZone: 消息。如果集合里的对象遵循 NSCopying 协议,那么对象就会被深复制到新的集合。如果对象没有遵循 NSCopying 协议,而尝试用这种方法进行深复制,会在运行时出错。copyWithZone: 这种拷贝方式只能够提供一层内存拷贝(one-level-deep copy),而非真正的深复制。
第二个方法是将集合进行归档(archive),然后解档(unarchive),如:
NSData* pData = [NSKeyedArchiver archivedDataWithRootObject:oldArray];NSArray *arrTrueDeepCopy = [NSKeyedUnarchiver unarchiveObjectWithData:pData];
如果在多层数组中,对第一层进行内容拷贝,其它层进行指针拷贝,这种情况苹果认为这种复制不是真正的深复制,而是将其称为单层深复制(one-level-deep copy)。
针对于上面来说有人会这么划分:
浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制。
深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制。
完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制。
完。
- 深拷贝&&浅拷贝
- 深拷贝||浅拷贝
- 浅拷贝,深拷贝
- 浅拷贝,深拷贝
- 深拷贝,浅拷贝
- 浅拷贝 深拷贝
- 浅拷贝.深拷贝
- 浅拷贝 深拷贝
- 深拷贝,浅拷贝
- 深拷贝、浅拷贝
- 深拷贝、浅拷贝
- 深拷贝+浅拷贝
- 深拷贝 浅拷贝
- 浅拷贝、深拷贝
- 浅拷贝、深拷贝
- 深拷贝、浅拷贝
- 深拷贝,浅拷贝
- 深拷贝、浅拷贝
- GPS坐标转换
- 给定一个整数,如何取其各个位的数
- 针对Logstash吞吐量一次优化
- Android反编译
- c语言==8位数据越界处理(20)
- 深拷贝、浅拷贝
- Android activity的生命周期
- 猫客论坛上线
- LDA主题聚类学习小结
- 扫描(Scanner类)
- 系统操作日志设计
- SpringBoot之配置文件及自定义参数
- 习题 3.1
- Exception in thread "http-bio-8080-exec-3"内存溢出