含弱引用可选属性的类深拷贝问题:父属性赋值后为空
问题分析与解决方案
你的核心问题出在**weak属性的ARC特性**以及拷贝时未正确建立强引用链:parent是弱引用,若没有其他强引用指向拷贝后的父节点对象,它会被ARC立即释放,最终变为nil。同时你之前的拷贝逻辑存在混乱,比如重复创建拷贝、未正确维护父子间的强引用关系,导致parent无法被保留。
先修正基础定义
首先统一代码里的拼写(你代码里混用了child/childs,这里统一为children),并添加Equatable实现用于定位目标节点:
class A: Equatable { var children: [A] = [] weak var parent: A? // 根据实际需求判断节点相等,这里用对象引用判断 static func == (lhs: A, rhs: A) -> Bool { return lhs === rhs } }
正确实现深拷贝逻辑
重新实现NSCopying,让拷贝逻辑分层处理:节点拷贝只创建空壳,父子关系由上层节点递归建立,确保强引用链完整:
extension A: NSCopying { // 基础拷贝:只创建当前节点的空实例,不处理父子关系 func copy(with zone: NSZone? = nil) -> Any { A() } // 深拷贝整个树形结构的核心方法 func deepCopy() -> A { let nodeCopy = self.copy() as! A // 递归处理子节点:每个子节点深拷贝后,绑定父节点并加入children数组(强引用) for child in self.children { let childCopy = child.deepCopy() childCopy.parent = nodeCopy nodeCopy.children.append(childCopy) } return nodeCopy } }
获取目标节点的拷贝
修正copyOfChildObject逻辑:先深拷贝整个树,再在拷贝后的树中定位目标节点,此时目标节点的parent已被正确绑定且不会被释放:
extension A { var copyOfChildObject: A? { guard let root = findRoot() else { return nil } // 深拷贝整个根节点树 let rootCopy = root.deepCopy() // 在拷贝后的树中找到对应原节点的拷贝 return findTargetNode(original: self, in: rootCopy) } // 找到当前节点的根节点 private func findRoot() -> A? { var current: A? = self while let parentNode = current?.parent { current = parentNode } return current } // 在拷贝树中递归查找目标节点 private func findTargetNode(original: A, in copyNode: A) -> A? { if original == copyNode { return copyNode } for childCopy in copyNode.children { if let target = findTargetNode(original: original, in: childCopy) { return target } } return nil } }
为什么这样能解决问题?
- 强引用链完整:父节点拷贝的
children数组会强引用子节点拷贝,子节点拷贝的parent(弱引用)指向父节点拷贝,既避免了循环引用,又保证父节点拷贝不会被ARC释放。 - 拷贝逻辑分层:基础
copy()只负责创建节点,deepCopy()递归维护父子关系,逻辑清晰,不会出现重复创建或引用丢失的问题。 - 目标节点定位准确:通过
Equatable的引用判断,能精准找到拷贝后的目标节点,此时它的parent已经被上层节点正确设置。
内容的提问来源于stack exchange,提问作者user16016486
相关产品推荐
相关产品推荐

