iOS基础之深复制与浅复制

来源:互联网 发布:编写c语言程序步骤 编辑:程序博客网 时间:2024/06/06 02:05
有关深复制与浅复制:
对象复制有两种,一种是foudation中自己的对象,包括NSString,NSArray,NSMutableArray,NSDictionary,等等,第二种就是自定义对象的复制;其实要实现复制,必须要实现两个协议,对于前者,系统已经默认给实现了,而对于后者则必须自己实现。
首先,说说第一种(NSString等)对象复制的各种情况,举例如下:

       #import <Foundation/Foundation.h>

#import "Car.h"

int main(int argc, const char * argv[])

{


    @autoreleasepool {

        

        // insert code here...

        

        NSArray *array1 = [[NSArray allocinitWithObjects:@"one",@"two",@"three",@"four"nil];

        NSMutableArray *array2 = [[NSMutableArray alloc]initWithObjects:@"one",@"two",@"three",@"four"nil];

        NSArray *array1Copy = [array1 copy];

        NSArray *array2Copy = [array2 copy];

        NSArray *array1MutableCopy = [array1 mutableCopy];

        NSArray *array2MutableCopy = [array2 mutableCopy];

        NSLog(@"array1地址:%parray2地址:%p,array1Copy地址:%parray2Copy地址:%parray1MutableCopy地址:%parray2MutableCopy地址:%p",array1,array2,array1Copy,array2Copy,array1MutableCopy,array2MutableCopy);

    }

    return 0;

}

       

结果说明:将第一种又可分为两种,一种是不可变如NSArray,一种是可变如NSMutableArray。

a:对于不可变的对象,如果进行copy复制,那么此时的copy相当于retain,并没有开辟新的内存,但是如果是进行mutableCopy则是进行了新内存的开辟,此时只是计数加1而已;

b:对于可变对象,则不管是copy还是mutableCopy,都开辟了新的内存空间。

特别说明:尽管上面的b重新开辟了内存,但是其也只是浅复制,因为他只是复制了这个对象,而对于这个对象中的属性并没有进行复制。

对于第二种自定义的对象,要实现复制则要实现

 

#import

#import "Car.h"

int main(int argc, const char * argv[])

{


    @autoreleasepool {    

        Car *car = [[Car alloc]init];

        car.price = @"12";

        Car *carCopy = [car copy];

                NSLog(@"car地址:%p,carCopy地址:%p,carPrice地址:%p,carCopyPrice地址:%p",car,carCopy,car.price,carCopy.price);

    }

    return 0;

}

-----------------------------------------

 

#import


@interface Car : NSObject<</span>NSCopying,NSMutableCopying>

@property (nonatomic,retain) NSString *price;

@property (nonatomic,copy) NSString *name;

@end

---------------------------------------

 

#import "Car.h"


@implementation Car


@synthesize name = _name,price = _price;

-(id)copyWithZone:(NSZone *)zone{

        第一种

    Car *car = [[[self class] allocWithZone:zone] init];

    car.name = _name;

    car.price = _price;


    第二种

     Car *car = [[[self classallocWithZone:zone] init];

     car.name = [_name copy];

     car.price = [_price copy];

 

    

    //第三种

     Car *car = [[[self classallocWithZone:zone] init];

    car.name = [_name mutableCopy];

    car.price = [_price mutableCopy];

    return car;

}


-(id)mutableCopyWithZone:(NSZone *)zone{

    Car *car = [[[self classallocWithZone:zone] init];

    

    return car;

}

@end

对于第一种,实现的是浅拷贝,因为在协议方法中,并没有进行属性的复制;

对于第二种,实现的其实也是浅拷贝,这其实就是上面的a中所说的情况,属性是NSString类型,进行copy其实就相当于retain

对于第三种,实现的则是深拷贝,这时候不管是对象还是对象的属性都开辟了新内存;

第一第二种打印结果:car地址:0x7fed71c143a0,carCopy地址:0x7fed71c14ac0,carPrice地址:0x10bf6a388,carCopyPrice地址:0x10bf6a388

第三种打印结果:2013-06-17 20:45:49.722 CopyTest[620:403] car地址:0x106f143a0,carCopy地址:0x106f14ad0,carPrice地址:0x106dd3388,carCopyPrice地址:0x106f14db0



如果不懂再来看一下下面的一段代码:

[cpp] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2.   
  3. int main(int argc, const char * argv[])  
  4. {  
  5.   
  6.     @autoreleasepool {  
  7.           
  8.         NSMutableArray *dataArray = [NSMutableArray arrayWithObjects:  
  9.                                      [NSMutableString stringWithString:@"one"],  
  10.                                      [NSMutableString stringWithString:@"two"],  
  11.                                      [NSMutableString stringWithString:@"three"],  
  12.                                      nil  
  13.                                      ];  
  14.         NSMutableArray *dataArray2;  
  15.         NSMutableString *mStr;  
  16.           
  17.         NSLog(@"dataArray:   ");  
  18.         for(NSString *elem in dataArray)  
  19.             NSLog(@"   %@", elem);  
  20.           
  21.         //执行一个拷贝,然后改变其中的一个字符串(浅复制)  
  22.         dataArray2 = [dataArray mutableCopy];  
  23.           
  24.         //这种方式会同时改变连个数组中的对象  
  25.         mStr = [dataArray objectAtIndex:0];  
  26.         [mStr appendString:@"ONE"];  
  27.                   
  28.         NSLog(@"dataArray:");  
  29.         for(NSString *elem in dataArray)  
  30.             NSLog(@"  %@",elem);  
  31.           
  32.         NSLog(@"dataArray2:");  
  33.         for(NSString *elem in dataArray2)  
  34.             NSLog(@"  %@",elem);  
  35.           
  36.         [dataArray2 release];  
  37.     }  
  38.     return 0;  
  39. }  

使用上述方法的执行结果如下:


注意:原始数组及其副本中的第一个元素的值:他们都被修改了。为什么会产生这样的结果呢?

或许你能理解为什么dataArray的第一个元素发生改变,但是不明白为什么它的副本也会改变。

这与默认的浅复制方式有关。它意味着使用mutableCopy方式复制数组时,在内存中为新的数组对象分配了空间,并且将单个元素复制到新的数组中。然而将原始数组中每个元素复制到新位置意味着:仅将引用从一个数组元素复制到另一个数组元素。这样做的最终结果,就是这两个数组中的元素都指向内存中的同一个字符串。这与将一个对象赋值给另一个对象没有什么不同。

要为数组中每个元素创建完全不同的副本,需要执行所谓的深复制。这就意味着要创建数组中的每个对象内容的副本,而不仅是这些对象的引用的副本(并且考虑一下,如果一个数组中的元素本身是数组对象时,深复制意味着该如何处理?)

假设想要更改其中一个集合而不是他的副本,那么可能要为单个元素创建自己的副本。例如假设想要更改代码中的dataArray2的第一个元素,但不更改dataArray的第一个元素,可以创建一个新的字符串(使用stringWithString:之类的方法)并将它存储到dataArray2的第一个位置,如下所示:

[cpp] view plaincopy
  1. //这种方式只会改变一个数组中的对象,而对另外一个没有影响  
  2.         mStr = [NSMutableString stringWithString:[dataArray2 objectAtIndex:0]];  
  3.         [mStr appendString:@"ONE"];  
  4.         [dataArray2 replaceObjectAtIndex:0 withObject:mStr];  

如果顺利的话,你会发现即使替换了数组中的对象之后,mStr和dataArray2的第一个元素仍指向内存中的同一个对象。这意味着随后在程序中对mStr做的任何修改,也将会更改数组 的第一个元素。如果这不是你想要的,则可以总是释放mStr,并分配新实例,因为对象会被replaceObject:atIndex:withObject:方法自动保持。