美文网首页
浅拷贝和深拷贝 & copy的内存管理

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

作者: 20b347b28fc9 | 来源:发表于2016-04-01 01:03 被阅读373次

    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;
    

    }

    相关文章

      网友评论

          本文标题:浅拷贝和深拷贝 & copy的内存管理

          本文链接:https://www.haomeiwen.com/subject/dvfylttx.html