浅拷贝和深拷贝 & copy的内存管理

2016-04-01  本文已影响373人  20b347b28fc9

copy


本文旨在解决以下问题:


copy基本概念

浅拷贝和深拷贝

深拷贝和浅拷贝区别:

结论:只有当不可变的源对象通过copy方法创建了一个不可变的副本对象,才是浅拷贝。

copy的内存管理

NSString *str1 = [[NSString alloc] initWithFormat:@"erer"];       
NSString *str2 = [str1 copy];       
[str1 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;

}

上一篇下一篇

猜你喜欢

热点阅读