Objective C中对象的复制


Objective C中对象的复制

内容概述

  • 概述
  • 对象的浅复制和深复制
  • NSCopying协议和NSMutableCopying协议

15.1 概述

基本数据类型之间变量的赋值是值传递,而引用类型对象之间对象的赋值是引用传递。这样如果一个对象赋值给另外一个对象,当其中一个对象属性被修改时,另外一个对象的属性也同时被修改。下图描述了对象赋值的过程。

15.1 对象赋值过程

上图所示,定义一个Person对象,有两个属性name和age。假设per1对象的地址是0x123,将per1赋值给per2,是将per1的地址赋值给per2。所以per1和per2地址相同它们指向同一个对象。这样当第一个对象的属性改变时,第二个对象也会跟着改变。为了解决这个问题,使得第一个对象改变时不会影响到第二个对象,我们需要复制对象。

// 测试赋值

-(void)testAssign{

    // 实例化Person

    Person *per1 = [[Person alloc]init];

    // 给第一个对象属性赋值

    per1.age = 20;

    per1.name = @"tom";

    // 将第一个对象引用赋值给第二个对象

    Person *per2 = per1;

    per1.name = @"big tom";

    per1.age = 21;

    NSLog(@"per1's name=%@,age=%d",per1.name,per1.age);

    NSLog(@"per2's name=%@,age=%d",per2.name,per2.age);

}

上述程序输出结果如下:

2013-04-15 13:49:18.840 chapter15-01[1001:303] per1's name=big tom,age=21

2013-04-15 13:49:18.842 chapter15-01[1001:303] per2's name=big tom,age=21

这个问题同样出现在集合数组中,我们将一个数组赋值给另外一个数组,当删除一个数组中的一个元素时,第二个数组中的元素也将被删除。

// 测试赋值2

-(void)testAssign2{

    // 初始化数组

    NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"1",@"2",@"3", nil];

    // 赋值

    NSMutableArray *array2 = array1;

    // 删除第二个数组中的元素

    [array2 removeObjectAtIndex:0];

    // 遍历第一个数组

    for(NSString *item in array1){

        NSLog(@"%@",item);

    }

    NSLog(@"%@",@"------------------");

    // 遍历第二个数组

    for(NSString *item in array2){

        NSLog(@"%@",item);

    }

}

程序输出结果如下所示。

2013-04-15 13:52:42.704 chapter15-01[1019:303] 2

2013-04-15 13:52:42.705 chapter15-01[1019:303] 3

2013-04-15 13:52:42.706 chapter15-01[1019:303] ------------------

2013-04-15 13:52:42.706 chapter15-01[1019:303] 2

2013-04-15 13:52:42.706 chapter15-01[1019:303] 3

15.2 对象的浅复制和深复制

为了避免在15.1中出现的问题,我们可以复制对象。NSObject提供了两个方法,- (id)copy和- (id)mutableCopy,copy方法可以拷贝一个不可变对象,而mutableCopy可以拷贝一个可变对象,例如NSMutableArray、NSMutableSet等。

下面是一个NSMutableArray,我们使用mutableCopy方法拷贝一个对象赋值给一个新对象,这样当从第一个数组中删除一个元素时,第二个数组并没有发生改变。

// 测试拷贝

-(void)testCopy{

    // 初始化数组

    NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"1",@"2",@"3", nil];

    // 拷贝数组

    NSMutableArray *array2 = [array1 mutableCopy];

    // 删除第二个数组中的元素

    [array2 removeObjectAtIndex:0];

    // 遍历第一个数组中的元素

    for(NSString *item in array1){

        NSLog(@"%@",item);

    }

    NSLog(@"%@",@"------------------");

    // 遍历第二个数组中的元素

    for(NSString *item in array2){

        NSLog(@"%@",item);

    }

}

上述程序的输出结果如下所示。

2013-04-15 14:01:39.739 chapter15-01[1037:303] 1

2013-04-15 14:01:39.741 chapter15-01[1037:303] 2

2013-04-15 14:01:39.741 chapter15-01[1037:303] 3

2013-04-15 14:01:39.742 chapter15-01[1037:303] ------------------

2013-04-15 14:01:39.742 chapter15-01[1037:303] 2

2013-04-15 14:01:39.742 chapter15-01[1037:303] 3

对象的复制分为深复制和浅复制,浅复制只复制对象本身,而对象包含或关联的对象并不进行复制。而深复制不光复制对象本身,对象包含或关联的对象将被同时复制。

下面我们定义一个可变数组,可变数组中添加若干可变字符串。如果进行浅复制数组中的元素将会受到影响,如果进行深赋值数组中的元素将不会受到影响。

NSMutableArray *array1 = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"1"],[NSMutableString stringWithString:@"2"],[NSMutableString stringWithString:@"3"], nil];

// 浅复制

// NSMutableArray *array2 = [array1 mutableCopy];

// 深复制

NSMutableArray *array2 = [NSMutableArray arrayWithCapacity:3];

// 赋值数组中的每一个元素

for(NSString *str2 in array1){

    [array2 addObject:[str2 mutableCopy]];

}

// 改变第一个数组中的元素

NSMutableString *str = [array1 objectAtIndex:0];

[str appendString:@"changed"];

// 遍历改变后第一个数组中的内容

for(NSString *item in array1){

    NSLog(@"%@",item);

}

NSLog(@"%@",@"------------------");

// 遍历改变后第二个数组中的内容

for(NSString *item in array2){

    NSLog(@"%@",item);

}

程序运行结果如下所示。

2013-04-15 14:11:10.695 chapter15-01[1051:303] 1changed

2013-04-15 14:11:10.697 chapter15-01[1051:303] 2

2013-04-15 14:11:10.698 chapter15-01[1051:303] 3

2013-04-15 14:11:10.698 chapter15-01[1051:303] ------------------

2013-04-15 14:11:10.698 chapter15-01[1051:303] 1

2013-04-15 14:11:10.698 chapter15-01[1051:303] 2

2013-04-15 14:11:10.699 chapter15-01[1051:303] 3

15.3 NSCopying协议和NSMutableCopying协议

在前两节中我们使用数组NSArray来模拟对象的拷贝,我们查看源码,发现NSArray类实现了NSCopying协议和NSMutableCopying协议。

@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>

- (NSUInteger)count;

- (id)objectAtIndex:(NSUInteger)index;

@end

如果是我们自己定义的一个类,要想进行对象复制,我们也必须实现NSCopying协议或NSMutableCopying协议,否则,程序将运行失败。

下面我们定义一个Person类,实现NSCopying协议,并实现了(id)copyWithZone:(NSZone *)zone方法。

#import <Foundation/Foundation.h>

// 实现NSCopying协议

@interface Person : NSObject <NSCopying>

// age 属性

@property int age;

// name属性

@property (nonatomic,copy) NSString *name;

@end

#import "Person.h"

@implementation Person

@synthesize age;

@synthesize name;

-(void)setName:(NSString *)name1{

    name = [name1 copy];

}

// 覆盖拷贝方法

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

    // NSZone是一个内存区域对象

    Person *per = [[Person allocWithZone:zone]init];

    return per;

}

@end

Person *per = [[Person alloc]init];

// 如果没有实现Copying协议会出现错误

Person *per2 = [per copy];

另外,在声明属性时可以使用copy关键字设置属性的set方法的赋值使用copy对象。

@property (nonatomic,copy) NSString *name;

-(void)setName:(NSString *)name1{

    name = [name1 copy];

}