浅拷贝:只拷贝引用(指针), 不拷贝对象空间(不创建新的对象空间,只是使对象的引用计数器加1)
深拷贝:不拷贝引用,拷贝的是整个对象空间,即拷贝时会创建新的对象空间,空间内容和原来内容相同。
copy mutableCopy对于自定义对象是一样的
copy mutableCopy对于系统的基本类是有区别的
NSString NSMutableString NSArray NSMutableArray NSDictionary NSMutableDictionary NSSet NSMutableSet NSData NSMutableData
拷贝对象的时候要向对象发送copy或mutableCopy消息,执行copy或mutableCopy的对象必须要遵守NSCopying协议或NSMutableCopying协议,并实现里面的方法:copyWithZone:或mutableCopyWithZone:
系统的一些类之所以可以copy或mutableCopy,只因为这个类本身已经遵守了协议并实现了协方法,我们可以直接对其copy或mutableCopy, 但是我们自定义的类如果想要 copy ,就要遵守协议并实现协议方法。对于自定义的类或系统的其他类是没有可变和不可变之说的,copy和mutableCopy的功能是一样的。
系统类的copy
NSArray *array = [NSArray arrayWithObjects:@"one", @"two", nil];
NSLog(@"array == address: %p retainCount: %ld", array, array.retainCount);
NSArray *array2 = [array copy];
NSLog(@"array2 == address: %p retainCount: %ld", array2, array2.retainCount);
// Foundation基础类,字符串,数组,字典,集合,不可变类,调用copy,是浅拷贝,甚至可以理解为它的作用相当于retain
// 注意这里浅拷贝有两个条件:
// 第一是这些Foundation基础类
// 第二是不可变对象
// 第二是调用copy方法
NSMutableArray *mArr = [[NSMutableArray alloc] initWithObjects:@"dog", @"cat", nil];
NSLog(@"mArr == address: %p retainCount: %ld", mArr, mArr.retainCount);
NSMutableArray *mArr2 = [mArr copy];
NSLog(@"mArr2 == address: %p retainCount: %ld", mArr2, mArr2.retainCount);
// 总结:对于系统的这些可变对象,调用copy时,是深拷贝,对象的内容被copy
————————————————————————
自定义对象的copy
#import <Foundation/Foundation.h>
@interface Dog : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic) NSInteger age;
@property (nonatomic) NSInteger height;
@end
#import "Dog.h"
@implementation Dog
// 调用copy的时候自动调用
- (id)copyWithZone:(NSZone *)zone {
return [self retain]; // 不会创建新的对象,只是让对象的引用计数器加1
}
// 调用mutableCopy的时候自动调用
- (id)mutableCopyWithZone:(NSZone *)zone {
return [self retain];
}
- (void)dealloc {
NSLog(@"Dog dealloc");
[super dealloc];
}
@end
#import <Foundation/Foundation.h>
#import "Dog.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Dog *dog = [[Dog alloc] init];
dog.age = 2;
dog.height = 170;
NSLog(@"dog: %p retainCount: %ld", dog, dog.retainCount);
Dog *newDog = [dog copy];
NSLog(@"newDog: %p retainCount: %ld", newDog, newDog.retainCount);
}
return 0;
}
————————————————————————
对于自定义类的深拷贝,需要创建新的空间,并把原来的对象的属性赋给它即可,比如对于上面的示例,如果要对dog深拷贝,就要这样实现:
// 调用copy的时候自动调用
- (id)copyWithZone:(NSZone *)zone {
// return [self retain]; // 不会创建新的对象,只是让对象的引用计数器加1
Dog *copyDog = [[[self class] allocWithZone:zone] init];
copyDog.age = self.age;
copyDog.height = self.height;
return copyDog;
}
// 调用mutableCopy的时候自动调用
- (id)mutableCopyWithZone:(NSZone *)zone {
// return [self retain];
Dog *copyDog = [[[self class] allocWithZone:zone] init];
copyDog.age = self.age;
copyDog.height = self.height;
return copyDog;
}
————————————————————————
注意:在自定义类的拷贝中,对于浅拷贝,在拷贝过程中,如果对象内容(对象的属性)中有其他的对象引用(指针),那么只拷贝指针引用,不拷贝指针指向的对象空间。对于深拷贝,如果对象内容(对象的属性)中有其他的对象引用,那么要拷贝掼针指向的对象空间,而不是引用。如果指针指向的对象又不其他对象指针,则继续拷贝空间,递归下去,直到拷贝到最后。那么如果有一个对象,对它深拷贝,就不仅要仅仅拷贝当前对象了,对象中如果有其他指引指向了另外一个对象,那么这个对象也应该继续深拷贝:
Car.h
#import <Foundation/Foundation.h>
#import "Engine.h"
@interface Car : NSObject <NSCopying>
@property (nonatomic) float speed;
@property (nonatomic, retain) Engine *engine;
@end
Car.m
#import "Car.h"
@implementation Car
- (id)copyWithZone:(NSZone *)zone {
Car *copyCar = [[self class] allocWithZone:zone];
copyCar.speed = self.speed;
Engine *newEngine = [self.engine copy];
copyCar.engine = newEngine;
[newEngine release];
// copyCar.engine = [[self.engine copy] autorelease];
return copyCar;
}
- (void)dealloc {
NSLog(@"car release");
self.engine = nil;
[super dealloc];
}
@end
Engine.h
#import <Foundation/Foundation.h>
@interface Engine : NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic) float weight;
@end
Engine.m
#import "Engine.h"
@implementation Engine
- (id)copyWithZone:(NSZone *)zone {
Engine *copyEngine = [[[self class] allocWithZone:zone] init];
copyEngine.name = self.name;
copyEngine.weight = self.weight;
return copyEngine;
}
- (void)dealloc {
NSLog(@"engine release");
self.name = nil;
[super dealloc];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Engine *engine = [[Engine alloc] init];
Car *car = [[Car alloc] init];
car.engine = engine;
[engine release];
NSLog(@"car == address: %p retainCount: %ld", car, car.retainCount);
NSLog(@"car.engine == address: %p retainCount: %ld", car.engine, car.engine.retainCount);
Car *copyCar = [car copy];
NSLog(@"copyCar == address: %p retainCount: %ld", copyCar, copyCar.retainCount);
NSLog(@"copyCar.engine == address: %p retainCount: %ld", copyCar.engine, copyCar.engine.retainCount);
[car release];
[copyCar release];
}
return 0;
}
car == address: 0x10011bfb0 retainCount: 1
car.engine == address: 0x10011bdb0 retainCount: 1
copyCar == address: 0x100401c90 retainCount: 1
copyCar.engine == address: 0x100401d30 retainCount: 1
car release
engine release
car release
engine release