You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift4中如何在NSOutlineView中展示TLV解析结果?

嘿,我来帮你搞定TLV解析结果在NSOutlineView里的展示问题,一步步来:

第一步:优化你的Node类

你现有的Node初始化方法太零散了,没法一次性创建完整的节点,而且缺少便捷的子节点管理方法。咱们调整一下,给它加个全能初始化器和addChild方法,这样构建树结构会顺手很多:

class Node: NSObject {
    var isConstructed = false
    var tag = "Tag"
    var length = 0
    var value = "Value"
    var children = [Node]()
    weak var parent: Node?

    override init() {
        super.init()
    }

    // 一次性设置所有核心属性的初始化器
    init(tag: String, length: Int, value: String, isConstructed: Bool) {
        self.tag = tag
        self.length = length
        self.value = value
        self.isConstructed = isConstructed
        super.init()
    }

    // 快速添加子节点,自动维护父子关系
    func addChild(_ node: Node) {
        node.parent = self
        children.append(node)
    }

    func isLeaf() -> Bool {
        return children.isEmpty
    }
}

第二步:从TLV解析结果构建节点树

你的解析器输出的是扁平的TLV字符串,咱们得把它转换成层级化的Node树。这里有个关键点:构造类型的Tag(比如<e1>、<ef>)它的Value里包含了其他子TLV,这些就是父节点,里面的子TLV是它们的子节点。

最优的方式是直接修改你的TLV解析器,在解析过程中就构建Node树——比如遇到构造类型的Tag时,递归解析它的Value,生成子节点并添加到当前节点下。如果暂时不想改解析器,也可以从输出的字符串反向解析(用正则匹配提取每个TLV块,再判断是否是构造节点,维护一个栈来管理父节点层级):

func buildTLVTree(from tlvOutput: String) -> [Node] {
    var rootNodes = [Node]()
    // 用正则匹配每个TLV块:<tag> length <value>
    guard let regex = try? NSRegularExpression(pattern: "<([a-f0-9]+)>\\s*(\\d+)\\s*<([^>]+)>", options: .caseInsensitive) else {
        return rootNodes
    }
    
    let matches = regex.matches(in: tlvOutput, range: NSRange(tlvOutput.startIndex..., in: tlvOutput))
    var nodeStack = [Node]() // 用栈维护当前的父节点层级
    
    for match in matches {
        // 提取Tag、Length、Value
        let tag = String(tlvOutput[Range(match.range(at: 1), in: tlvOutput)!])
        let length = Int(String(tlvOutput[Range(match.range(at: 2), in: tlvOutput)!])) ?? 0
        let value = String(tlvOutput[Range(match.range(at: 3), in: tlvOutput)!])
        
        // 判断是否是构造节点:Value里包含其他<tag>,说明有子节点
        let isConstructed = value.contains("<")
        let currentNode = Node(tag: tag, length: length, value: value, isConstructed: isConstructed)
        
        if isConstructed {
            // 构造节点作为父节点,添加到当前层级
            if let currentParent = nodeStack.last {
                currentParent.addChild(currentNode)
            } else {
                rootNodes.append(currentNode)
            }
            nodeStack.append(currentNode)
        } else {
            // 叶子节点,添加到当前父节点下
            if let currentParent = nodeStack.last {
                currentParent.addChild(currentNode)
                
                // 这里可以补充逻辑:当父节点的所有子节点处理完毕后,弹出栈回到上一级
                // 比如根据TLV的Length校验,子节点的总字节数等于父节点的Value长度时,弹出
            } else {
                rootNodes.append(currentNode)
            }
        }
        
        // 补充:如果当前节点是某个父节点的最后一个子节点,弹出栈(需要结合TLV规则判断,比如你解析器的输出顺序是层级化的,所以可以根据下一个节点的层级调整)
    }
    
    return rootNodes
}

第三步:配置NSOutlineView的数据源和委托

接下来就是把Node树和NSOutlineView绑定起来,实现数据源和委托方法:

数据源方法(处理节点层级)

extension YourViewController: NSOutlineViewDataSource {
    func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
        if let node = item as? Node {
            return node.children.count
        }
        // 返回根节点数量
        return rootNodes.count
    }
    
    func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
        if let node = item as? Node {
            return node.children[index]
        }
        return rootNodes[index]
    }
    
    func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
        guard let node = item as? Node else { return false }
        return !node.isLeaf()
    }
}

委托方法(处理单元格显示)

先在Storyboard里给NSOutlineView添加三列,分别命名为Tag、Length、Value,并给单元格设置标识符TLVCell,然后实现:

extension YourViewController: NSOutlineViewDelegate {
    func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
        guard let node = item as? Node else { return nil }
        guard let cellView = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("TLVCell"), owner: self) as? NSTableCellView else {
            return nil
        }
        
        switch tableColumn?.identifier.rawValue {
        case "Tag":
            cellView.textField?.stringValue = node.tag
        case "Length":
            cellView.textField?.stringValue = "\(node.length)"
        case "Value":
            cellView.textField?.stringValue = node.value
        default:
            break
        }
        return cellView
    }
}

最后小提示

比起从输出字符串反向解析,直接修改你的TLV解析器,在解析过程中构建Node树会更可靠——比如解析构造类型Tag时,直接递归解析它的Value生成子节点,这样能准确维护层级关系,避免字符串解析可能出现的错误。

内容的提问来源于stack exchange,提问作者Fritz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:33:47