Ios Swift中的递归函数在使用大量输入调用时崩溃
我有来自API的注释,这些注释要么是根注释,要么是子注释。这些评论是从API中无序出现的,我必须对它们进行排序 评论如下所示:Ios Swift中的递归函数在使用大量输入调用时崩溃,ios,swift,performance,recursion,crash,Ios,Swift,Performance,Recursion,Crash,我有来自API的注释,这些注释要么是根注释,要么是子注释。这些评论是从API中无序出现的,我必须对它们进行排序 评论如下所示: struct Comment: Codable, Equatable { var depth = 0 var id: Int = 0 var parent: Int? var content: String = "" var created: Int = 0 var up: Int = 0 var down: In
struct Comment: Codable, Equatable {
var depth = 0
var id: Int = 0
var parent: Int?
var content: String = ""
var created: Int = 0
var up: Int = 0
var down: Int = 0
var confidence: Double = 0
var name: String = ""
var mark: Int = 0
enum CodingKeys: String, CodingKey {
case id = "id"
case parent = "parent"
case content = "content"
case created = "created"
case up = "up"
case down = "down"
case confidence = "confidence"
case name = "name"
case mark = "mark"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(Int.self, forKey: .id)
parent = try values.decodeIfPresent(Int.self, forKey: .parent)
content = try values.decode(String.self, forKey: .content)
created = try values.decode(Int.self, forKey: .created)
up = try values.decode(Int.self, forKey: .up)
down = try values.decode(Int.self, forKey: .down)
confidence = try values.decode(Double.self, forKey: .confidence)
name = try values.decode(String.self, forKey: .name)
mark = try values.decode(Int.self, forKey: .mark)
}
init(with message: String, name: String, depth: Int) {
self.depth = depth
self.up = 1
self.content = message
self.name = name
}
}
private func split(_ comments: [Comment]) {
let parentNodes = comments.filter { $0.parent == 0 }.map { Node(value: $0) }
let childNodes = comments.filter { $0.parent != 0 }.map { Node(value: $0) }
self.sortComments(parentNodes: parentNodes, childNodes: childNodes)
}
所以我开始制作一个树状结构,并将这些注释放入节点中:
fileprivate class Node<T> {
var value: T
weak var parent: Node?
var children: [Node] = []
init(value: T) {
self.value = value
}
func add(child: Node) {
children.append(child)
child.parent = self
}
}
extension Node where T == Comment {
func search(id: Int) -> Node? {
if id == self.value.id {
return self
}
for child in children {
if let found = child.search(id: id) {
return found
}
}
return nil
}
var description: String {
"Id: \(value.id), parent: \(value.parent ?? -1)"
}
}
extension Node where T: Equatable {
func search(value: T) -> Node? {
if value == self.value {
return self
}
for child in children {
if let found = child.search(value: value) {
return found
}
}
return nil
}
}
然后我用这个递归函数对它们的注释进行排序:
private func sortComments(parentNodes: [Node<Comment>], childNodes: [Node<Comment>]) {
let parentNodes = parentNodes
var childNodes = childNodes
if let firstChild = childNodes.first {
let parentId = firstChild.value.parent!
if let parentNode = parentNodes.first(where: { $0.value.id == parentId }) {
firstChild.value.depth = parentNode.value.depth + 1
parentNode.add(child: firstChild)
childNodes.removeFirst()
self.sortComments(parentNodes: parentNodes, childNodes: childNodes)
} else {
//Comment is child of child
//Search children for parent
//Search also parentNodes, they may have a child already that is the parent
//of the current child we are looking at
parentNodes.forEach {
if let foundNode = $0.search(id: parentId) {
firstChild.value.depth = foundNode.value.depth + 1
foundNode.add(child: firstChild)
childNodes.removeFirst()
self.sortComments(parentNodes: parentNodes, childNodes: childNodes)
}
}
childNodes.forEach {
if let foundNode = $0.search(id: parentId) {
firstChild.value.depth = foundNode.value.depth + 1
foundNode.add(child: firstChild)
childNodes.removeFirst()
self.sortComments(parentNodes: parentNodes, childNodes: childNodes)
}
}
}
} else {
let sortedNodes = parentNodes.sorted { $0.value.confidence > $1.value.confidence }
self.convertCommentNodesToArray(nodes: sortedNodes, currentArray: [])
}
}
private func排序建议(parentNodes:[Node],childNodes:[Node]){
让parentNodes=parentNodes
var childNodes=childNodes
如果让firstChild=childNodes.first{
让parentId=firstChild.value.parent!
如果让parentNode=parentNodes.first(其中:{$0.value.id==parentId}){
firstChild.value.depth=parentNode.value.depth+1
parentNode.add(子项:firstChild)
childNodes.removeFirst()
self.sortComments(parentNodes:parentNodes,childNodes:childNodes)
}否则{
//评论是孩子的孩子
//在子项中搜索父项
//同时搜索parentNodes,它们可能已经有一个子节点作为父节点
//我们正在关注的当前孩子的
parentNodes.forEach{
如果让foundNode=$0.search(id:parentId){
firstChild.value.depth=foundNode.value.depth+1
foundNode.add(子项:firstChild)
childNodes.removeFirst()
self.sortComments(parentNodes:parentNodes,childNodes:childNodes)
}
}
childNodes.forEach{
如果让foundNode=$0.search(id:parentId){
firstChild.value.depth=foundNode.value.depth+1
foundNode.add(子项:firstChild)
childNodes.removeFirst()
self.sortComments(parentNodes:parentNodes,childNodes:childNodes)
}
}
}
}否则{
让sortedNodes=parentNodes.sorted{$0.value.confidence>$1.value.confidence}
self.convertCommentNodeStoreArray(节点:sortedNodes,currentArray:[]))
}
}
最后,我将节点转换为数组,以便TableView可以显示数据:
private func convertCommentNodesToArray(nodes: [Node<Comment>], currentArray: [Comment]) {
var nodes = nodes
var commentsArray = currentArray
if let firstNode = nodes.first {
commentsArray.append(firstNode.value)
if firstNode.children.count > 0 {
let remainingNodes = nodes.dropFirst()
let sortedChildren = firstNode.children.sorted { $0.value.confidence > $1.value.confidence }
convertCommentNodesToArray(nodes: sortedChildren + remainingNodes, currentArray: commentsArray)
} else {
nodes.removeFirst()
convertCommentNodesToArray(nodes: nodes, currentArray: commentsArray)
}
} else {
self.comments.value = commentsArray
}
}
private func convertCommentNodeStoreArray(节点:[Node],当前数组:[Comment]){
变量节点=节点
var commentsArray=currentArray
如果让firstNode=nodes.first{
append(firstNode.value)
如果firstNode.children.count>0{
让remainingNodes=nodes.dropFirst()
让sortedChildren=firstNode.children.sorted{$0.value.confidence>$1.value.confidence}
convertCommentNodesToArray(节点:sortedChildren+remainingNodes,currentArray:commentsArray)
}否则{
nodes.removeFirst()
convertCommentNodesToArray(节点:节点,currentArray:commentsArray)
}
}否则{
self.comments.value=commentsArray
}
}
这段代码可以很好地处理大量的注释。但在某个时刻,当我面对数千条注释时,这个函数会因为stackoverflow崩溃而崩溃
所以我的问题是:我如何使这个函数的性能更好,这样它就不会崩溃?我很高兴听到您的建议:)上一个函数中有几点可以针对堆栈溢出崩溃进行一些优化: 迭代算法与递归算法的比较 在提到内存优化之前,先看看这两种算法:
- 迭代方法可能看起来更难看、更难阅读,但它对内存大小几乎没有限制(基本上,您拥有的内存与设备提供的内存一样多)
- 相反,递归方法通常更干净,但受到堆栈大小(总是比整个内存大小小得多)的限制
值类型与参考类型 但是循环的数量并不是导致堆栈溢出的唯一原因。如果在堆栈中放入太多变量,也可能发生这种情况。如果您考虑一下存储在哪里的内容,就可以解决这个问题 值类型总是在堆栈中分配,而引用类型通常在堆中分配。(这并不总是正确的,但我想你现在可以忽略它) 因此,对于初学者,您可以考虑将
Comment
struct实现为一个类。
或者,至少您可以尝试通过将整个数组赋回堆栈中的另一个局部变量(再次)来避免在每个周期重复整个数组
var commentsArray = currentArray
例如,在上面这一行中,您不仅复制了数组(这是一种值类型),而且还复制了数组中的每个注释(因为注释是值类型)。您在每个周期上复制它们,使堆栈的大小呈指数级增长
为了避免重复注释,您只需使用类(这样您将只复制它们的引用),但如果您也希望避免重复数组,您可以将其作为inout参数传递即可
同样的