NSCopying协议
- 若想令自己所写的对象具有拷贝功能,则需要实现NSCopying协议。
- (id)copyWithZone:(NSZone *)zone;
- 方法中应该用全能初始化方法,来初始化待拷贝的对象。
//.h
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject <NSCopying>
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
- (id)initWithFirstName:(NSString*)firstName
andLastName:(NSString*)lastName;
@end
//.m
#import "EOCPerson.h"
@interface EOCPerson ()
@property (nonatomic,readwrite,strong) NSMutableArray *friends;
@end
@implementation EOCPerson
- (instancetype)initWithName:(NSString *)name age:(int)age
{
self = [super init];
if (self) {
self.name = name;
self.age = age;
_friends = [NSMutableArray array];
}
return self;
}
- (id)copyWithZone:(NSZone *)zone{
Person *p = [[[self class] allocWithZone:zone] initWithName:_name age:_age];
return p;
}
@end
- 如果全能初始化不能满足要求,还应该手动的加上一些操作。
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject <NSCopying>
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
- (id)initWithFirstName:(NSString*)firstName
andLastName:(NSString*)lastName;
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
@end
//.m
@interface EOCPerson ()
@property (nonatomic,readwrite,strong) NSMutableArray *friends;
@end
@implementation EOCPerson
- (id)initWithFirstName:(NSString*)firstName
andLastName:(NSString*)lastName {
if ((self = [super init])) {
_firstName = [firstName copy];
_lastName = [lastName copy];
_friends = [NSMutableSet new];
}
return self;
}
- (void)addFriend:(EOCPerson*)person {
[_friends addObject:person];
}
- (void)removeFriend:(EOCPerson*)person {
[_friends removeObject:person];
}
- (id)copyWithZone:(NSZone*)zone {
EOCPerson *copy = [[[self class] allocWithZone:zone]
initWithFirstName:_firstName
andLastName:_lastName];
copy->_friends = [_friends mutableCopy];//额外的代码
return copy;
}
@end
- 如果自定义对象分为可变版本和不可变版本,那么就要同时实现NSCopying与NSMutableCopying协议。
深拷贝与浅拷贝
-
浅拷贝:指针拷贝,不产生新的对象,源对象的引用计数器+1
-
深拷贝:对象拷贝,会产生新的对象,源对象的引用计数器不变
copy与mutableCopy
- copy:拷贝的结果是一个不可变(imutable)的对象, 无论源对象是可变的还是不可变的,copy之后的都是不可变的类型
不可变类型 变量名 = [不可变类型 copy];
不可变类型 变量名 = [可变类型 copy];
- mutableCopy:可变拷贝的结果的数据类型是一个可变的对象,无论源对象时不可变的还是可变的,可变拷贝之后的数据类型都是可变类型
可变类型 变量名 = [不可变类型 mutableCopy];
可变类型 变量名 = [可变类型 mutableCopy];
-
copy对引用计数器的影响:
-
拷贝一个不可变的类型的结果是新对象和源对象都指向同一个内存地址,即使指针拷贝,属于浅拷贝,所以不生产新对象,源对象的引用计数+1
-
拷贝一个可变的类型,会生成一个新对象,不影响源对象的引用计数
-
-
mutableCopy对引用计数器的影响:
-
无论对可变类型或者对不可变类型使用mutableCopy操作,都不会影响源对象的引用计数。
系统非容器类对象的 copy 操作
-
对系统非容器类不可变对象调用Copy方法其实只是把当前对象的指针指向了原对象的地址。
-
调用mutableCopy方法则是新分配了一块内存区域并把新对象的指针指向了这块区域。
-
对于可变对象来说调用Copy和MutableCopy方法都会重新分配一块内存。
-
copy和mutableCopy的区别在于copy在复制对象的时候其实是返回了一个不可变对象,因此当调用方法改变对象的时候会崩溃。
-
总结
- [immutableObject copy] 浅复制
- [immutableObject mutableCopy] 深复制
- [mutableObject copy] 深复制
- [mutableObject mutableCopy] 深复制
系统容器类对象的 copy 操作
-
对系统容器类不可变对象调用Copy方法其实只是把当前对象的指针指向了原对象的地址。
-
调用mutableCopy方法则是新分配了一块内存区域并把新对象的指针指向了这块区域。
-
对于系统容器类可变对象来说调用Copy和MutableCopy方法都会重新分配一块内存。
-
对于系统容器类可变对象,虽然重新分配了一块内存,但是对象里面的数据依然是指针复制的。
-
总结
- [immutableObject copy] 浅复制(对象地址和被复制的对象地址一样)
- [immutableObject mutableCopy] 单层深复制
- [mutableObject copy] 单层深复制
- [mutableObject mutableCopy] 单层深复制
自定义对象的 copy 操作
-
在iOS中并不是所有对象都支持Copy和MutableCopy,遵循NSCopying协议的类可以发送Copy协议,遵循NSMutableCopying协议的类可以发送MutableCopy消息。
-
如果一个对象没有遵循这两个协议而发送Copy或者MutableCopy消息那么会发生异常。
-
如果要遵循NSCopying协议,那么必须实现copyWithZone方法。
-
如果要遵循NSMutableCopying协议那么必须实现mutableCopyWithZone方法。
-
对于自定义对象来说调用Copy和MutableCopy方法都会重新分配一块内存。
-
总结
-
[customObject copy] 深复制
-
[customObject mutableCopy] 深复制
-
要点
-
若想令自己所写的对象具有拷贝功能,则需要实现NSCopying协议。
-
如果自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCopying与NSMutableCopying协议。
-
复制对象时需决定采用浅拷贝还是深拷贝,一般情况下应该尽量执行浅拷贝。
-
如果你所写的对象需要深拷贝,那么可考虑新增一个专门执行深拷贝的方法。
网友评论