美文网首页
iOS之深浅拷贝

iOS之深浅拷贝

作者: Coder_Cat | 来源:发表于2018-08-15 18:09 被阅读13次

    1.深拷贝和浅拷贝概念

    深拷贝和浅拷贝
    • 由上面的图我们可以明确地看出:

      1. 浅拷贝(Shallow copy):是指针复制,它们指向共同的内存地址,没有开辟新的空间,当内存销毁的时候,指向这片内存的几个指针需要重新定义才可以使用,要不然会成为野指针。相当于对象做一次retain操作,引用计数加1。

      2. 深拷贝(Deep copy):是指内容拷贝,拷贝后的对象会分配新的内存空间,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。源对象的引用计数不变,副本对象的引用计数为1。

      3.区别总结:浅拷贝就是指针拷贝,深拷贝就是内容拷贝;质区别在于是否开启新的内存空间、是否影响内存地址的引用计数。

    2.copy和mutableCopy

    • copy拷贝出来的对象类型总是不可变类型,
    • mutableCopy拷贝出来的对象类型总是可变类型(下面示1例可以证明)。

    3.OC中的非集合类对象和集合类对象的深浅拷贝

    一. 非集合类对象:NSString,NSMutableString,NSData,NSNumber
    非集合对象的copymutableCopy
    示例1:

            NSString *str1 = @"字符串1";
            NSString *str2 = [str1 copy];
            NSMutableString *str3 = [str1 mutableCopy];
            
            NSMutableString * str4 = [NSMutableString stringWithString:@"字符串2"];
            NSString * str5 = [str4 copy];
            NSMutableString *str6 = [str4 mutableCopy];
    

    打断点lldb调试:

    非集合对象的copy与mutableCopy.png
    • 在此解释一下在runtimeNSString的“真身”是__NSCFConstantStringNSMutableString的“真身”是__NSCFString,然后我们就能很清楚的看到,只要是copy得到的值就是不可变类型,而mutablecopy得到的是可变类型。但是str5 的类型显示是__NSCFString类型,应该是__NSCFConstantString类型才对啊,这里是为什么呢?个人猜测是子类指针指向父类(继承多太的一种体现),其本质还是父类,调NSMutableString的特有方法如运行 [str5 appendFormat:@"sss"]的时候会crash,原因就是str5本质是NSString类型不能调用子类NSMutableString特有的方法。
    • 结果分析:
      1.对不可变非集合对象进行 copy ,属于浅拷贝,不产生新的对象。
      2.对不可变非集合对象 进行 mutableCopy ,属于深拷贝,产生新的可变对象。
      3.对 可变非集合对象 进行 copy, 属于深拷贝,产生新的不可变对象。
      4.对可变非集合对象进行mutableCopy,属于深拷贝, 产生新的可变对象。
      说明:上面所说的对象其实不大合适,对象本质就是指针,上面所说的对象其实是指分配的内存,便于理解。
      二.集合类对象:NSArray,NSMutableArray,NSDictionary,NSSet...
      集合对象的copymutableCopy
      示例2:
            NSArray * arr1 = [NSArray arrayWithObjects:@1,@2,@3,nil];
            NSArray * arr2 = [arr1 copy];
            NSMutableArray * arr3 = [arr1 mutableCopy];
            
            NSMutableArray * arr4 = [NSMutableArray arrayWithObjects:@4,@5,@6,nil];
            NSArray * arr5 = arr4.copy;
            NSMutableArray * arr6 = arr4.mutableCopy;
    

    打断点lldb调试:

    集合对象的copy与mutableCopy.png
    • 结果分析:
      1.对不可变集合对象进行 copy ,属于浅拷贝,不产生新的对象。
      2.对不可变集合对象 进行 mutableCopy ,属于单层深拷贝,产生新的可变对象。
      3.对 可变集合对象 进行 copy, 属于单层深拷贝,产生新的不可变对象。
      4.对可变集合对象进行mutableCopy,属于单层深拷贝, 产生新的可变对象。
      单层深拷贝:是指集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制,列如上面的arr3[arr1 mutableCopy]产生的,产生的arr3是可变的数组,arr3里面的NSNumber元素只是指针复制并没有进行深复制。
    • 不管是集合对象还是非集合对象都遵从以下规律:
    源对象类型 拷贝方式 目标对象类型 是否开辟新内存 拷贝类型
    不可变对象 copy 不可变对象 浅拷贝(指针拷贝)
    不可变对象 mutableCopy 可变对象 深拷贝(内容拷贝)
    可变对象 copy 不可变对象 深拷贝(内容拷贝)
    可变对象 mutableCopy 可变对象 深拷贝(内容拷贝)

    4.property中copy关键字

    • 当我们使用一个copy关键字声明一个对象的时候, 调用 setter 方法的时候,copy关键字会为对象自动copy一个副本。
      示例3:
    @property (nonatomic, copy) NSArray *array;
    
    - (void)setArray:(NSArray *)array {
    _array = [array copy];  
    }
    

    此时不管传进来的arrayNSArray还是NSMutableArray类型都会产生一个array copy 了一个副本,是不可变的。

    如果我们直接用strong关键字的话,又是怎样的呢?
    示例4:

    @property (nonatomic, strong) NSArray *array;
    
    - (void)setArray:(NSArray *)array {
    _array = array;  
    }
    

    如果此时传入的array是一个NSMutableArray的话,self.array可能会在不知情的情况下被修改。

    • 为什么用@property声明的NSString(或NSArrayNSDictionary)等不可变属性经常使用copy关键字?使用strong关键字,会有什么问题?
      示例5:
    @interface Person : NSObject
    @property(nonatomic ,strong) NSString * name;
    @property(nonatomic ,copy) NSString * age;
    @end
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person * p = [[Person alloc]init];
            NSMutableString * name = [NSMutableString stringWithFormat:@"马云爸爸"];
            p.name = name;
            NSLog(@"name修改前:%@", p.name);
            [name appendString:@"和马化腾岳父谁厉害?"];
            NSLog(@"name修改后:%@", p.name);
            
            NSMutableString * age = [NSMutableString stringWithFormat:@"马云爸爸50岁"];
            p.age = age;
            NSLog(@"age修改前:%@", p.age);
            [age appendString:@"马化腾岳父45岁"];
            NSLog(@"age修改后:%@", p.age);
        }
        return 0;
    }
    

    打印结果

    property中copy关键字.png
    结果分析:Personname属性用了strong修饰,当传进来的NSMutableString类型的name变量值改变后Personname属性值也发生了改变(父类指针指向子类,多态),而我们并没有对Personname属性直接操作,这样是不合理的;而Personage属性用了copy修饰,不管传进来的变量是不是可变的字符串,都只会拷贝一份指针而指向该块内存,当可变的字符串age值发生改变时(内存发生了改变),属性age还是指向该块内存,不会改变。
    • 如果用copy修饰可变对象会产生什么后果?
      假如我们用copy关键字 来声明一个NSMutableString对象。
      示例6:
    @interface Person : NSObject
    @property(nonatomic ,copy) NSMutableString * name;
    @end
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person * p = [[Person alloc]init];
            NSMutableString * name = [NSMutableString stringWithFormat:@"马云爸爸"];
            p.name = name;
            [p.name appendString:@"和马化腾岳父谁厉害?"];
        }
        return 0;
    }
    
    copy修饰可变对象.png
    crash原因是:尝试使用appendString改变不可变对象

    5.自定义对象使用copy和mutableCopy。

    如果想自定义对象使用copymutableCopy,只需要分别遵守NSCopying,NSMutableCopying协议,然后实现- (id)copyWithZone:(nullable NSZone *)zone;- (id)mutableCopyWithZone:(nullable NSZone *)zone;方法。
    示例7:

    #import <Foundation/Foundation.h>
    @interface Person : NSObject<NSCopying,NSMutableCopying>
    @property(nonatomic ,copy) NSString * name;
    @property(nonatomic ,copy) NSString * age;
    @end
    
    #import "Person.h"
    @implementation Person
    -(id)copyWithZone:(NSZone *)zone{
        Person * p = self;
        return p;
    }
    -(id)mutableCopyWithZone:(NSZone *)zone{
        Person * p = [[Person allocWithZone:zone] init];
        p.name = self.name;
        p.age = self.age;
        return p;
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person * p1 = [[Person alloc]init];
            p1.name = @"马云";
            p1.age = @"55";
            Person * p2 = [p1 copy];
            Person * p3 = [p1 mutableCopy];
        }
        return 0;
    }
    
    自定义对象使用copy和mutableCopy.png

    相关文章

      网友评论

          本文标题:iOS之深浅拷贝

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