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

UITableView与UITextField卡顿问题求助:多JSON映射优化

问题描述

我有19个JSON文件,加载后根据UITextField输入的字符执行映射操作。当输入大量字符(比如100个)或滚动UITableView时,UI出现卡顿、停滞现象。尽管尝试用后台线程处理,问题仍未解决,还存在内存管理问题,试过存储到Core Data但优化失败。


现有代码

数据加载与转换逻辑

func loadBigDataFiles() {
        // Record the start time
        let startTime = Date()

        // Prepare file names for font files
        let fontFiles = (0...19).map { "font\($0).json" }
        var inputStreams: [InputStream] = []

        // Create InputStream for each JSON file
        for fileName in fontFiles {
            if let fileURL = Bundle.main.url(forResource: fileName, withExtension: nil),
               let stream = InputStream(url: fileURL) {
                inputStreams.append(stream)
            } else {
                print("Failed to create InputStream for file \(fileName)")
            }
        }

        // Load fonts from the input streams
        load(inputStreams: inputStreams)

        // Record the end time
        let endTime = Date()

        // Calculate the execution time
        let executionTime = endTime.timeIntervalSince(startTime)
        print("Execution time: \(executionTime) seconds")
    }
    func convertData(indexNo: Int, text: String) -> String {
        // Fetch the hash map for the specified index
        let hashMap = fonts[indexNo]
        
        // Initialize an empty result string
        var result = ""
        
        // Iterate over each character in the input text
        for char in text {
            // Convert the character to uppercase and fetch its mapped value from the hashMap
            if let mappedValue = hashMap[char.uppercased().first!] {
                // Append the mapped value to the result, followed by a newline
                result += mappedValue + "\n"
            }
        }
        
        // Return the final result string
        return result
    }


    // Load fonts from input streams
    func load(inputStreams: [InputStream]) {
        for stream in inputStreams {
            do {
                let json = try getJson(from: stream)
                var map: [Character: String] = [:]
                for char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
                    if let bigText = json[String(char)] {
                        map[char] = bigText
                    }
                }
                fonts.append(map)
            } catch {
                print("Load error: \(error)")
            }
        }
        loaded = true
    } 

UITableView实现代码

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         return 20
     }
     
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! BigTextTableViewCell
             
             // Set a placeholder or loading state initially
             cell.configure(name: "Loading...", currentRow: "\(indexPath.row)")
             
             // Asynchronously convert data
             let text = textField.text!
             let index = indexPath.row
             
//         DispatchQueue.global(qos: .background).async { [self] in
                 // Perform the conversion in a background thread
                 let finalString = bigText.convertData(indexNo: index, text: text)
                 
                 DispatchQueue.main.async {
                     // Update the cell with the result on the main thread
                     if let updatedCell = tableView.cellForRow(at: indexPath) as? BigTextTableViewCell {
                         updatedCell.configure(name: finalString, currentRow: "\(indexPath.row)")
                     }
//                 }
             }
         return cell
     }

JSON示例数据

{
  "A": "───\r\n╔═╗─\r\n║╬╚╗\n╚══╝",
  "B": "╔╗─\r\n╚╗║\r\n║╬║\r\n╚═╝",
  "C": "───\r\n╔═╗\r\n║═╣\r\n╚═╝",
  "D": "─╔╗\r\n╔╝║\r\n║╬║\r\n╚═╝",
  "E": "───\r\n╔═╗\r\n║╩╣\r\n╚═╝",
  "F": "╔═╗\r\n║═╣\r\n║╔╝\r\n╚╝─",
  "G": "╔═╗\r\n║╬║\r\n╠╗║\r\n╚═╝",
  "H": "╔╗─\r\n║╚╗\r\n║║║\r\n╚╩╝",
  "I": "╔╗\r\n╠╣\r\n║║\r\n╚╝",
  "J": "─╔╗\r\n─╠╣\r\n╔╝║\r\n╚═╝",
  "K": "╔╗─\r\n║╠╗\r\n║═╣\r\n╚╩╝",
  "L": "───\r\n╔╗─\r\n║╚╗\r\n╚═╝",
  "M": "────\r\n╔══╗\r\n║║║║\r\n╚╩╩╝",
  "N": "────\r\n╔═╦╗\r\n║║║║\r\n╚╩═╝",
  "O": "───\r\n╔═╗\r\n║╬║\r\n╚═╝",
  "P": "╔═╗\r\n║╬║\r\n║╔╝\r\n╚╝─",
  "Q": "╔═╗\r\n║╬║\r\n╚╗║\r\n─╚╝",
  "R": "───\r\n╔╦╗\r\n║╔╝\r\n╚╝─",
  "S": "╔═╗\r\n║═╣\r\n╠═║\r\n╚═╝",
  "T": "╔╗─\r\n║╚╗\r\n║╔╣\r\n╚═╝",
  "U": "───\r\n╔╦╗\r\n║║║\r\n╚═╝",
  "V": "─────\r\n╔═╦═╗\r\n╚╗║╔╝\r\n─╚═╝─",
  "W": "────\r\n╔╦╦╗\r\n║║║║\r\n╚══╝",
  "X": "───\r\n╔╦╗\r\n╠║╣\r\n╚╩╝",
  "Y": "╔╦╗\r\n║║║\r\n╠╗║\r\n╚═╝",
  "Z": "╔═╗\r\n╠═║\r\n║═╣\r\n╚═╝",
  " ": "───\r\n───\r\n───\r\n───"
}

解决方案

1. 修复后台线程执行逻辑

你的TableView代码中,后台线程的执行代码被注释掉了,导致字符串转换操作在主线程执行,这是卡顿的核心原因。恢复后台线程逻辑,同时避免循环引用:

DispatchQueue.global(qos: .userInitiated).async { [weak self] in
    guard let self = self, let text = self.textField.text else { return }
    let finalString = self.bigText.convertData(indexNo: index, text: text)
    
    DispatchQueue.main.async {
        if let updatedCell = tableView.cellForRow(at: indexPath) as? BigTextTableViewCell {
            updatedCell.configure(name: finalString, currentRow: "\(indexPath.row)")
        }
    }
}

使用qos: .userInitiated优先级,比.background更适合用户触发的即时操作。

2. 优化字符串拼接性能

convertData中用result +=频繁创建新字符串(Swift字符串是值类型,每次拼接都会复制),改用数组收集片段再一次性拼接:

func convertData(indexNo: Int, text: String) -> String {
    let hashMap = fonts[indexNo]
    var parts: [String] = []
    parts.reserveCapacity(text.count) // 预分配容量,减少内存分配次数
    
    for char in text {
        guard let uppercasedChar = char.uppercased().first, 
              let mappedValue = hashMap[uppercasedChar] else { continue }
        parts.append(mappedValue)
    }
    
    return parts.joined(separator: "\n")
}

该优化能大幅减少内存拷贝开销,提升转换速度。

3. 缓存转换结果

重复转换相同文本是无效开销,添加缓存字典存储已转换的结果:

private var conversionCache: [String: String] = [:]

func convertData(indexNo: Int, text: String) -> String {
    let cacheKey = "\(indexNo)_\(text)"
    if let cachedResult = conversionCache[cacheKey] {
        return cachedResult
    }
    
    let hashMap = fonts[indexNo]
    var parts: [String] = []
    parts.reserveCapacity(text.count)
    
    for char in text {
        guard let uppercasedChar = char.uppercased().first, 
              let mappedValue = hashMap[uppercasedChar] else { continue }
        parts.append(mappedValue)
    }
    
    let result = parts.joined(separator: "\n")
    conversionCache[cacheKey] = result
    return result
}

在文本框内容变化时清空缓存,避免内存占用过高:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    conversionCache.removeAll()
    tableView.reloadData()
    return true
}

4. 提升TableView滚动流畅度

  • 用UITextView替代UILabel显示长字符串,渲染效率更高,同时设置textView.isScrollEnabled = false。
  • 给TableView设置自动高度:
    tableView.estimatedRowHeight = 200
    tableView.rowHeight = UITableView.automaticDimension
    
  • 简化BigTextTableViewCell的自动布局约束,减少渲染时的计算量。

5. 内存优化

  • 加载JSON时,将[Character: String]改为[String: String],避免字符类型转换开销,字典查找效率一致。
  • 合并19个JSON文件为一个,减少IO操作次数,加载时一次性解析。
  • 无需使用Core Data,只读数据直接内存缓存即可,Core Data会增加额外的性能开销。

内容的提问来源于stack exchange,提问作者Muhammad Bilal Raza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 04:45:53