浅拷贝和深拷贝 & copy的内存管理
2016-04-01 本文已影响373人
20b347b28fc9
copy
本文旨在解决以下问题:
- 1.区别浅拷贝和深拷贝
- 2.copy的内存管理
- 3.@property中的copy关键字的使用
- 4.copy与block
- 5.如何自定义类实现copy操作
copy基本概念
- copy:创建不可变副本
- 使用copy,该类需要遵守NSCopying协议, 实现copyWithZone:方法
- mutableCopy:创建可变副本
- 需要遵守NSMutableCopying协议,实现mutableCopyWithZone:方法
浅拷贝和深拷贝
-
浅复制:浅拷贝,指针拷贝,shallow copy
- 不产生新的对象,源对象和副本对象是同一对象
- 源对象(即副本对象)引用计数器+1,相当于做了一次retain
-
深复制:深拷贝,内容拷贝,deep copy
- 产生新的对象,源对象和副本对象不是同一个对象
- 源对象引用计数器不变,副本对象计数器为1(因为是新的)
深拷贝和浅拷贝区别:
- 根本就是:在于产生的副本对象跟原对象会不会一个改动了,另一个跟着变
结论:只有当不可变的源对象通过copy方法创建了一个不可变的副本对象,才是浅拷贝。
copy的内存管理
- 浅拷贝
- 原有对象引用计数器+1
- 必须对源对象(即副本对象)做一次release
NSString *str1 = [[NSString alloc] initWithFormat:@"erer"];
NSString *str2 = [str1 copy];
[str1 release];
- 深拷贝
- 源对象引用计数器不变,副本对象计数器为1
- 需要对副本对象做一次release
NSString *str1 = [[NSString alloc] initWithFormat:@"erer"];
NSMutableString *str2 = [str1 mutableCopy];
[str2 release];
@property中copy修饰字符
----用retain修饰字符串的情况下
@interface Person : NSObject
@property (nonatomic, retain) NSString *name;
@end
NSMutableString *str = [NSMutableString stringWithFormat:@"erer"];
Person *p = [[Person alloc] init];
p.name = str;
// person中的属性会被修改
[str appendString:@" cool"];
NSLog(@"name = %@", p.name);
内存分析
问题一:当修改外界str时,p.name也跟着修改了,非常不安全。
问题二:此时Person对象不会释放
----用copy修饰字符串的情况下
@interface Person : NSObject
@property (nonatomic, retain) NSString *name;
@end
NSMutableString *str = [NSMutableString stringWithFormat:@"erer"];
Person *p = [[Person alloc] init];
p.name = str;
// person中的属性不会被修改
[str appendString:@" cool"];
NSLog(@"name = %@", p.name);
内存分析
@property中copy修饰block
----不用copy(用assign)修饰block对象时
Person *p = [[Person alloc] init];
p.name = @"erer";
Dog *d = [[Dog alloc] init];
d.age = 10;
NSLog(@"retainCount = %lu", [d retainCount]); // 1
p.pBlock = ^{
// 报错, 调用之前就销毁了
NSLog(@"age = %d", d.age);
};
[d release]; // 0
p.pBlock();
[p release];
我们不能确定什么时候调用block,而在block内部访问对象d时,对象可能已经释放,报野指针错误
----用copy修饰block对象时
Person *p = [[Person alloc] init];
p.name = @"erer";
Dog *d = [[Dog alloc] init];
d.age = 10;
NSLog(@"retainCount = %lu", [d retainCount]); // 1
p.pBlock = ^{
//block在堆中,block访问外界对象,会对使用到的外界对象进行一次retain
NSLog(@"age = %d", d.age);
NSLog(@"retainCount = %lu", [d retainCount]); // 1
};
[d release]; // 1
p.pBlock();
[p release];
block可以保住对象的命,但会让对象计数器+1, 因此需要在对象的delloc方法中,给block发送一条release消息。
@implementation Person
- (void)dealloc
{
// 只要给block发送一条release消息, block中使用到的对象也会收到该消息
Block_release(_pBlock);
[super dealloc];
}
@end
``
blcok补充
- block默认存储在栈中,栈中的block访问到外界的对象,不会对对象进行retain
- block如果在堆中,如果block访问了外界的对象,会对外界的对象进行一次retain
##### ----总结:@property内存管理策略选择
- 非ARC
- copy : 只用于NSString\block
- retain : 除NSString\block以外的OC对象
- assign :基本数据类型、枚举、结构体(非OC对象),当2个对象相互引用,一端用retain,一端用assign
- ARC
- copy : 只用于NSString\block
- strong : 除NSString\block以外的OC对象
- weak : 当2个对象相互引用,一端用strong,一端用weak
- assgin : 基本数据类型、枚举、结构体(非OC对象)
### 自定义类实现copy操作
- 让类遵守NSCopying协议
- 实现 copyWithZone:方法,在该方法中返回一个对象的副本即可。
- 在copyWithZone方法中,创建一个新的对象,并设置该对象的数据与现有对象一致, 并返回该对象.
zone: 表示空间,分配对象是需要内存空间的,如果指定了zone,就可以指定 新建对象对应的内存空间。但是:zone是一个非常古老的技术,为了避免在堆中出现内存碎片而使用的。在今天的开发中,zone几乎可以忽略
##### ----无父类实现
-(id)copyWithZone(NSZone *)zone{
CustomMode *custom = [[[self class] copyWithZone:zone] init];
Custom ->_a = [_a copyWithZone:zone];
Custom -> _c = _c;//不是对象的 直接赋值
Return custom;
}
##### ----有父类实现
![
![Snip20160331_9.png](https://img.haomeiwen.com/i704596/4d15c7bef4d17d12.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)](https://img.haomeiwen.com/i704596/18f4448954d1dde0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 不调用父类方法, 无法拷贝父类中继承的属性
- 不重新父类copyWithZone, 无法拷贝本来中的特有属性
-(id)copyWithZone(NSZone *)zone{
CustomModel *custom = [super copyWithZone:zone];
Return custom;
}