您的位置:首页 > 大数据 > 人工智能

由NSString的copy和strong/retain引出o-c的copy机制 (二)

2014-03-11 15:20 369 查看
上篇分析了NSString的copy和retain的区别,并且引出对oc中的copy原理探究的欲望,参考了很多资料,这里做讨论。

1、实现了NSCopy/NSMutableCopying的框架类

我们都知道oc框架里面的例如NSString、NSArray等很多类的对象在需要复制的时候都可以直接调用[obj copy/mutablecopy]方法。调用copy方法时,会向NSCopying的协议方法copywithzone发消息的。作为根类的NSObject并没有实现NSCopying协议,所以他们都默认实现了NSCopying和NSMutableCopying协议,实现copywithzone和mutablecopywithzone方法。

@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>

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

@interface NSDictionary : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>
2、自定义的对象的copy/mutablecopy

我们自定义的对象要想调用copy/mutablecopy方法,也必须手动实现NSCopying协议,实现两个方法。如果没有实现,调用copy方法会抛出异常。

定义一个person类

@interface Person : NSObject
@property (strong,nonatomic) NSString* name;
@property (assign,nonatomic) NSInteger age;
@property (strong,nonatomic) NSArray* sons;
@end
//**************
@implementation Person

@end


使用:

1 Person* p = [[Person alloc] init];
2 p.name = @"张三";
3 p.age = 50;
4 p.sons = @[@"张小一",@"张小二"];
5 Person* copy = [p copy];
到5的地方抛出异常:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person copyWithZone:]: unrecognized select
or sent to instance 0x8b240d0'
无法识别的方法消息,这里因为person没有实现NSCopying协议和coypwithzone方法。接下来我为person类添加协议和方法。

@interface Person : NSObject<NSCopying,NSMutableCopying>
...
...
...
@end

//**************************************
@implementation Person


-(id)copyWithZone:(NSZone *)zone

{

Person* copy = [[self class] allocWithZone:zone]; //分配新的内存空间 对于self本身来说,属于深拷贝了。

copy.name = [self.name copy]; //NSString的copy,对于name,属于浅拷贝,因为都属于不可变

copy.age = self.age;

copy.sons = [self.sons copy]; //同样浅拷贝

return self;

}

-(id)mutableCopyWithZone:(NSZone *)zone

{

Person* copy = [[self class] allocWithZone:zone];//分配新的内存空间

copy.name = [self.name mutableCopy]; //深拷贝 不可变拷贝可变

copy.age = self.age;

copy.sons = [self.sons mutableCopy]; //同样深拷贝

return copy;

}

@end




2.1使用copy:

Person* p = [[Person alloc] init];
p.name = @"张三";
p.age = 50;
p.sons = @[@"张小一",@"张小二"];
Person* copy = [p copy];


打印地址:

NSLog(@"p addr = %p",p);

NSLog(@"copy addr = %p",copy);


输出内容:

p addr = 0x8b30140
copy addr = 0x8d34750


我们看到,地址不同,copy的内存空间是新开辟的。这里要说一下,因为一般我们copy出来的Person对象,副本变动不希望影响到另一个。所以在copywithzone里面如上面那样操作,新开辟内存空间,逐个成员变量赋值;如果copy的对象不考虑互相影响的后果,则可以直接:

-(id)copyWithZone:(NSZone *)zone
{
return self;
}
不过这样貌似没有什么意义,不如直接用:

Person* copy = p;


Person的name和sons都属于不可变,并且调用的是copy,所以对象p和copy的这两个成员的地址应该是一样的。 我们打印出name和sons的值和地址来看一下:

NSLog(@"p.name addr = %p && copy.name addr = %p",p.name,copy.name);
NSLog(@"p.sons addr = %p && copy.sons addr = %p",p.sons,copy.sons);

NSLog(@"p's son addr = %p && copy's son addr = %p",[p.sons objectAtIndex:0],[copy.sons objectAtIndex:0]);
输出:

p.name addr = 0x4848 && copy.name addr = 0x4848
p.sons addr = 0x8d46410 && copy.sons addr = 0x8d46410

p's son addr = 0x5858 && copy's son addr = 0x5858
这个结果没有悬念,遵循不可变对象copy属于浅拷贝。

这里注意到,我专门打印出p.sons 和copy.sons的第一个元素地址来做对比,他们是一样的。NSArray是容器类型,里面的元素是在copy的时候是什么样的情况,后面详细讨论。

2.2使用mutablecopy

可以看到,copywithzone和mutablecopywithzone都创建了新的内存空间,不同之处在于对成员变量的处理上。

Person* copy = [p mutableCopy];
打印结果:

p.name addr = 0x5848 && copy.name addr = 0x8a3db30
p.sons addr = 0x8a3f8f0 && copy.sons addr = 0x8a3f020
p's son addr = 0x5858 && copy's son addr = 0x5858
结果是name和sons都被深拷贝,sons里面元素仍然是浅拷贝,看来NSArray的mutablecopy不会令其中的元素深拷贝。

3、容器类对象的拷贝 参考:/article/4950670.html

对于类似NSArray、NSDictionary之类的容器元素,要想实现深拷贝,可以参考一下两种方法:

1 NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"],[NSStringstringWithString:@"b"],@"c",nil];
2 NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
3 NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
4 [NSKeyedArchiver archivedDataWithRootObject: array]];


trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则不是,对于deepCopyArray内的不可变元素其还是指针复制。或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。举个例子,[[array objectAtIndex:0]appendstring:@”sd”]后其他的容器内对象并不会受影响。[[array objectAtIndex:1]和[[deepCopyArray
objectAtIndex:0]尽管是指向同一块内存,但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全意义上的深拷贝,但是apple的官方文档将其列为deep copy了,并添加了copy和mutablity的关系说明,故在此做一说明


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: