gpt4 book ai didi

ios - 如何使用 copyWithZone 进行深拷贝以复制结构?

转载 作者:可可西里 更新时间:2023-11-01 05:33:47 24 4
gpt4 key购买 nike

我有一个表示结构的类。

这个名为 Object 的类具有以下属性

@property (nonatomic, strong) NSArray *children;
@property (nonatomic, assign) NSInteger type;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, weak) id parent;

children 是其他 Object 的数组。 parent 是对对象父对象的弱引用。

我正在尝试复制并粘贴此结构的一个分支。如果选择了根对象,parent 显然是 nil。如果对象不是根,则它有一个父对象。

为了能够做到这一点,Object 类型的对象必须符合 NSCopyingNSCoding 协议(protocol)。

这是我在那个类上实现的这些协议(protocol)。

-(id) copyWithZone: (NSZone *) zone
{
Object *obj = [[Object allocWithZone:zone] init];
if (obj) {
[obj setChildren:_children];
[obj setType:_type];
[obj setName:_name];
[obj setParent:_parent];
}

return obj;
}

- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:@(self.type) forKey:@"type"];
[coder encodeObject:self.name forKey:@"name"];
NSData *childrenData = [NSKeyedArchiver archivedDataWithRootObject:self.children];
[coder encodeObject:childrenData forKey:@"children"];
[coder encodeConditionalObject:self.parent forKey:@"parent"]; //*
}

- (id)initWithCoder:(NSCoder *)coder {

self = [super init];

if (self) {

_type = [[coder decodeObjectForKey:@"type"] integerValue];
_name = [coder decodeObjectForKey:@"name"];
_parent = [coder decodeObjectForKey:@"parent"]; //*
NSData *childrenData = [coder decodeObjectForKey:@"children"];
_children = [NSKeyedUnarchiver unarchiveObjectWithData:childrenData];
_parent = nil;
}

return self;

}

您可能已经注意到,我没有引用在 initWithCoder:encodeWithCoder: 上检索或存储 self.parent,正因为如此,对象的每个子对象都带有 parent = nil。

我只是不知道如何存储它。仅仅因为这个。假设我有这个 Object 结构。

ObjectA > ObjectB > ObjectC

encoderWithCoder: 开始它的魔术编码 ObjectA 时,它也会编码,ObjectBObjectC 但当它开始编码 ObjectB 它找到一个指向 ObjectA 的父引用并将再次开始它,创建一个挂起应用程序的循环引用。我试过了。

我如何编码/恢复父引用?

我需要的是存储一个对象,并在恢复时恢复一个与存储的对象相同的新副本。我不想恢复存储的同一个对象,而是一个副本。

注意:我按照 Ken 的建议添加了标有 //* 的行,但是 _parentinitWithCoder 上为 nil : 对于应该有一个 parent

的对象

最佳答案

使用 [coder encodeConditionalObject:self.parent forKey:@"parent"] 对父对象进行编码。正常使用 -decodeObjectForKey: 对其进行解码。

这样做的目的是,如果父对象由于其他原因被归档——比如它是树中更高层对象的子对象——则恢复对父对象的引用。但是,如果父对象仅被编码为条件对象,则它不会存储在存档中。解码存档后,父级将为 nil

您对 children 数组进行编码的方式既笨拙又会阻止将父级正确编码为条件对象。由于您正在为 children 创建一个单独的存档,因此没有存档可能同时具有 Object 及其父级(无条件)。因此,当存档被解码时,不会恢复到父级的链接。您应该改为执行 [coder encodeObject:self.children forKey:@"children"]_children = [coder decodeObjectForKey:@"children"]

您的-copyWithZone: 实现有问题。副本与原始副本具有相同的子项,但子项不将副本视为其父项。同样,副本将原始对象的父对象视为其父对象,但该父对象不包括其子对象中的副本。这会让您感到悲伤。

一种选择是利用您的 NSCoding 支持来制作副本。您会对原件进行编码,然后对其进行解码以生成副本。像这样:

-(id) copyWithZone: (NSZone *) zone
{
NSData* selfArchive = [NSKeyedArchiver archivedDataWithRootObject:self];
return [NSKeyedUnarchiver unarchiveObjectWithData:selfArchive];
}

复制操作将复制整个子树。因此,它将拥有自己的子代,这些子代是原始子代的副本,等等。它没有父代。

另一种选择是只复制不属于包含树数据结构的原始部分(即 typename)。也就是说,副本最终将没有父项也没有子项,这是适当的,因为它不在树本身中,它只是当时碰巧在树中的事物的副本。

最后,-copyWithZone: 在分配新对象时应该使用[self class] 而不是 Object。这样,如果您曾经编写过 Object 的子类及其 -copyWithZone: 在设置子类的属性之前调用 super,此实现在Object 将分配正确类(子类)的实例。例如:

-(id) copyWithZone: (NSZone *) zone
{
Object *obj = [[[self class] allocWithZone:zone] init];
if (obj) {
[obj setType:_type];
[obj setName:_name];
}

return obj;
}

关于ios - 如何使用 copyWithZone 进行深拷贝以复制结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28823781/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com