好的,让我们更详细地探讨 iOS 中的浅拷贝和深拷贝,包括底层实现和源码分析。这里我们将分别探讨不可变对象和可变对象的拷贝,以及如何实现自定义对象的深拷贝。
浅拷贝(Shallow Copy)
浅拷贝只是复制对象的引用,而不复制其内容。这意味着新对象和原对象实际上指向同一个内存地址。对于不可变对象(如 NSString 和 NSArray),浅拷贝和深拷贝可能表现得一样,因为不可变对象的内容不会改变。
NSMutableArray 的浅拷贝
NSMutableArray *originalArray = [NSMutableArray arrayWithObjects:@"A", @"B", @"C", nil];
NSMutableArray *shallowCopyArray = [originalArray copy];
这里 copy 方法实际上调用了 copyWithZone: 方法。
- (id)copyWithZone:(NSZone *)zone {
return NSCopyObject(self, 0, zone);
}
NSCopyObject 是一个低级别的函数,用于创建对象的浅拷贝。对于不可变对象,如 NSArray,NSString 等,copy 方法返回的是同一个对象的引用。
id NSCopyObject(id obj, size_t extraBytes, NSZone *zone) {
if (obj == nil) return nil;
// Perform a shallow copy of the object
id result = objc_duplicate(obj, extraBytes, zone);
return result;
}
objc_duplicate 是一个底层函数,用于创建对象的浅拷贝。在更高层次上,Objective-C 的对象拷贝依赖于这个或类似的底层机制。由于 objc_duplicate 是一个私有函数,Apple 的公开文档并没有详细说明它的实现,但我们可以根据已知的 Objective-C 运行时库来推测其实现逻辑。
objc_duplicate 的假设实现
objc_duplicate 是一个底层函数,用于复制对象的内存。这个函数可能涉及以下几个步骤:
- 分配内存:为新对象分配内存。
- 复制内容:将原对象的内容复制到新对象中。
- 设置引用计数:初始化新对象的引用计数。
假设的 objc_duplicate 实现可能如下:
c
id objc_duplicate(id obj, size_t extraBytes, NSZone *zone) {
// 获取对象的类信息
Class cls = object_getClass(obj);
// 计算对象的大小
size_t size = class_getInstanceSize(cls) + extraBytes;
// 分配内存
id newObj = (id)calloc(1, size);
// 复制对象的内容
memcpy(newObj, obj, size);
// 设置对象的类
object_setClass(newObj, cls);
// 初始化引用计数(假设引用计数在对象头部)
if (newObj) {
objc_retain(newObj);
}
return newObj;
}
深拷贝(Deep Copy)
深拷贝会复制对象及其包含的所有子对象,还需要递归地复制其包含的所有子对象,生成一个全新的对象。深拷贝通常通过归档(Archiving)和解档(Unarchiving)实现。
NSMutableArray 的深拷贝
NSMutableArray *originalArray = [NSMutableArray arrayWithObjects:@"A", @"B", @"C", nil];
NSMutableArray *deepCopyArray = [[NSMutableArray alloc] initWithArray:originalArray copyItems:YES];
这里 initWithArray:copyItems: 方法的实现涉及到对每个元素进行深拷贝。
- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag {
self = [self initWithCapacity:[array count]];
if (self) {
for (id obj in array) {
id copyObj = flag ? [obj copy] : obj;
[self addObject:copyObj];
if (flag) [copyObj release];
}
}
return self;
}
归档和解档实现深拷贝
对于自定义对象,通常需要实现 NSCoding 协议,以支持归档和解档,从而实现深拷贝。
自定义对象实现深拷贝
@interface CustomObject : NSObject <NSCoding, NSCopying>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSMutableArray *items;
@end
@implementation CustomObject
// NSCoding协议中的方法
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.name forKey:@"name"];
[coder encodeObject:self.items forKey:@"items"];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
_name = [coder decodeObjectForKey:@"name"];
_items = [coder decodeObjectForKey:@"items"];
}
return self;
}
// NSCopying协议中的方法
- (id)copyWithZone:(NSZone *)zone {
CustomObject *copy = [[[self class] allocWithZone:zone] init];
copy.name = [self.name copy];
copy.items = [[NSMutableArray alloc] initWithArray:self.items copyItems:YES];
return copy;
}
@end
使用归档和解档进行深拷贝
CustomObject *originalObject = [[CustomObject alloc] init];
originalObject.name = @"Original";
originalObject.items = [NSMutableArray arrayWithObjects:@"Item1", @"Item2", nil];
// 深拷贝
NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:originalObject];
CustomObject *deepCopyObject = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData];
// 修改原始对象
originalObject.name = @"Modified";
[originalObject.items addObject:@"Item3"];
// 输出结果
NSLog(@"Original Object: %@", originalObject.items); // 输出: Original Object: (Item1, Item2, Item3)
NSLog(@"Deep Copy Object: %@", deepCopyObject.items); // 输出: Deep Copy Object: (Item1, Item2)
小结
浅拷贝:
- 浅拷贝只复制对象的引用,原对象和新对象共享相同的内存地址。
- 实现方式:通常通过
copy方法实现。 - 不可变对象的浅拷贝会返回同一对象的引用。
深拷贝:
- 深拷贝会复制对象及其包含的所有子对象,生成一个全新的对象。
- 实现方式:通常通过归档(Archiving)和解档(Unarchiving)实现,或者通过自定义的拷贝方法。
- 自定义对象需要实现
NSCoding和NSCopying协议以支持深拷贝。
通过深入理解浅拷贝和深拷贝的底层实现,可以更有效地管理内存和处理对象操作,避免因引用共享而导致的意外修改。