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
相关产品推荐
相关产品推荐

