美文网首页
iOS 深浅拷贝汇总

iOS 深浅拷贝汇总

作者: Accepted_ | 来源:发表于2020-08-15 23:12 被阅读0次

    概念:

    浅拷贝是指针拷贝,新指针和旧指针指向同一块地址。

    深拷贝将对象也进拷贝了一份到新的内存地址,新指针指向了新内存地址。

    但iOS中的深浅拷贝还分多种情况。

    一、容器类对象(Array、Dictionary、Set)和非容器类对象(String)。
    二、可变对象(NSArray、NSMutableDictionary、NSMutableSet、NSMutableString)和不可变对象(NSArray、NSDictionary、NSSet、NSString)。
    //本文中容器类对象只用Array举例,其余同理

    一、非容器类对象(以NSString和NSMutableString为例)

    1.1 不可变非容器类对象:NSString

            NSString* string =@"123";
            NSString* copyString = [string copy];
            NSString* mutCopyString = [string mutableCopy];
            NSLog(@"string:        %@ %p", string, string);
            NSLog(@"copyString:    %@ %p", copyString, copyString);
            NSLog(@"mutCopyString: %@ %p", mutCopyString, mutCopyString);
    
    【不可变】【非容器类对象】深浅拷贝打印结果
    • 不可变非容器类对象:
      copy->地址不变、指针复制
      mutCopy->地址改变、对象复制
    • 不可变非容器对象copy返回类型和原对象相同,mutCopy返回类型为__NSCFString
            NSString * string = @"123";
            NSString * formatString = [NSString stringWithFormat:@"123"];
    
    不可变非容器深浅拷贝返回的对象类型

    1.2 可变非容器类对象:NSMutableString

            NSMutableString * mutString = [NSMutableString stringWithString:@"123"];
            NSString* copyString = [mutString copy];
            NSMutableString* mutCopyString = [mutString mutableCopy];
            NSLog(@"mutString:    %@ %p", mutString, mutString);
            NSLog(@"copyString:    %@ %p", copyString, copyString);
            NSLog(@"mutCopyString: %@ %p", mutCopyString, mutCopyString);
    
            NSLog(@"----------append------------");
            [mutString appendString:@"9"];   //修改原字符串
            NSLog(@"mutString:    %@ %p", mutString, mutString);
            NSLog(@"copyString:    %@ %p", copyString, copyString);
            NSLog(@"mutCopyString: %@ %p", mutCopyString, mutCopyString);
    
    【可变】【非容器类对象】深浅拷贝打印结果
    • 可变非容器类对象:
      copy->地址改变、对象复制
      mutCopy->地址改变、对象复制
      修改原对象不会影响深浅拷贝后的对象。
    • 不可变非容器对象copy返回类型NSTaggedPointerString,mutCopy返回类型为__NSCFString 可变非容器深浅拷贝返回的对象类型

    二、容器类对象(以NSArray和NSMutableArray为例)

    2.1 不可变容器类对象:NSArray

            NSArray * array = @[[NSMutableString stringWithFormat:@"1"], @"2"];
            NSArray * copyArray = [array copy];
            NSArray * mutCopyArray = [array mutableCopy];
         
            NSLog(@"array:        %p", array);
            NSLog(@"copyArray:    %p", copyArray);
            NSLog(@"mutCopyArray: %p", mutCopyArray);
    
    【不可变】【容器类对象】深浅拷贝打印结果

    我们来改变一个容器中的元素。

            NSArray * array = @[[NSMutableString stringWithFormat:@"1"], @"2"];
            NSArray * copyArray = [array copy];
            NSArray * mutCopyArray = [array mutableCopy];
            
            NSLog(@"array:        %@", array);
            NSLog(@"copyArray:    %@", copyArray);
            NSLog(@"mutCopyArray: %@", mutCopyArray);
            
            NSLog(@"----------change------------");
            NSMutableString * mutString = array[0];
            [mutString appendString:@"9"];   //修改第一个元素
            NSLog(@"array:%@", array);
            NSLog(@"copyArray:%@ ", copyArray);
            NSLog(@"mutCopyArray:%@", mutCopyArray);
    
    【不可变】【容器类对象】深浅拷贝改变元素

    对比两张打印结果图可发现,深拷贝虽然地址改变,但修改原数组会影响深拷贝后的新数组。我猜想可能是数组为对象拷贝(地址改变),但数组元素仍旧是指针拷贝。我们打印一下元素地址证实下:

            NSArray * array = @[[NSMutableString stringWithFormat:@"1"], @"2"];
            NSArray * copyArray = [array copy];
            NSArray * mutCopyArray = [array mutableCopy];
            
            NSLog(@"array:        %p [0]%p [1]%p", array, array[0], array[1]);
            NSLog(@"copyArray:    %p [0]%p [1]%p", copyArray, copyArray[0], copyArray[1]);
            NSLog(@"mutCopyArray: %p [0]%p [1]%p", mutCopyArray, mutCopyArray[0], mutCopyArray[1]);
    
    【不可变】【容器类对象】深浅拷贝元素地址打印
    • 不可变容器类对象:
      copy->对象地址不改变(数组为指针拷贝)、元素值跟随原数组修改(元素也为指针拷贝)
      mutCopy->对象地址改变(数组为对象拷贝)、元素值跟随原数组修改(元素为指针拷贝)

    • 且要注意,copy返回的是不可变类型,mutCopy返回的是可变类型。 不可变容器深浅拷贝返回的对象类型

    2.2 可变容器类对象:NSMutableArray

            NSMutableArray * mutArray = [NSMutableArray arrayWithArray:@[[NSMutableString stringWithFormat:@"1"], @"2"]];
            NSArray * copyArray = [mutArray copy];
            NSArray * mutCopyArray = [mutArray mutableCopy];
            
            NSLog(@"array:        %p", mutArray);
            NSLog(@"copyArray:    %p", copyArray);
            NSLog(@"mutCopyArray: %p", mutCopyArray);
    
    【可变】【容器类对象】深浅拷贝元素地址打印

    我们再修改一个元素。

            NSMutableArray * mutArray = [NSMutableArray arrayWithArray:@[[NSMutableString stringWithFormat:@"1"], @"2"]];
            NSArray * copyArray = [mutArray copy];
            NSArray * mutCopyArray = [mutArray mutableCopy];
            
            NSLog(@"array:        %@", mutArray);
            NSLog(@"copyArray:    %@", copyArray);
            NSLog(@"mutCopyArray: %@", mutCopyArray);
            
            NSLog(@"----------change------------");
            [mutArray[0] appendString:@"9"];//修改数组元素
            NSLog(@"array:%@", mutArray);
            NSLog(@"copyArray:%@", copyArray);
            NSLog(@"mutCopyArray:%@", mutCopyArray);
            
            NSLog(@"----------change2------------");
            mutArray[0] = @"0"; //直接改变元素对象
            NSLog(@"array:%@", mutArray);
            NSLog(@"copyArray:%@", copyArray);
            NSLog(@"mutCopyArray:%@", mutCopyArray);
    
    【可变】【容器类对象】深浅拷贝改变元素

    可以看出,可变容器类对象无论是copy还是mutCopy都是深拷贝,但元素是指针拷贝。改变原数组元素的值还是会影响拷贝后的数组。但如果给原数组的元素赋值新对象,则不会影响。


    可变容器深浅拷贝返回的对象类型
    • 和不可变容器类对象相同,copy返回的是不可变类型,mutCopy返回的是可变类型。
    总结:

    对于不可变类型(无论容器类非容器类),copy是浅拷贝,mutCopy是深拷贝。
    对于可变类型(无论容器类非容器类),copy和mutCopy都是深拷贝。

    但对于容器类对象(Array、Dictionary、Set)来说,无论深拷贝还是浅拷贝,其元素都是指针拷贝。


    如何实现深拷贝(来源:深拷贝、浅拷贝

    实现深拷贝需要遵守NSCoying协议,实现- (id)copyWithZone:(NSZone *)zone方法。当对一个property属性含有copy修饰符的时候,在进行赋值操作的时候实际上就是调用这个方法。

    • 父类实现深拷贝时,子类如何实现深度拷贝?
      父类实现深拷贝之后,子类只要重写copyWithZone方法,在方法内部调用父类的copyWithZone方法,之后实现自己的属性的处理
    • 父类没有实现深拷贝时,子类如何实现深度拷贝?
      子类除了需要对自己的属性进行处理,还要对父类的属性进行处理。

    相关文章

      网友评论

          本文标题:iOS 深浅拷贝汇总

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