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

输入字符过快时DispatchWorkItem未及时取消引发CFString释放错误求解

解决快速搜索时的[CFString length]: message sent to deallocated instance崩溃问题

我帮你拆解下你遇到的问题,核心原因是后台任务取消不彻底+内存访问不安全,再加上快速输入时的任务堆积,最终导致了野指针访问崩溃。下面是具体的分析和解决方案:

问题根源拆解

  1. 未通过弱引用访问实例变量:在anyTranslate里你直接访问test和searchText,如果当前ViewController已经被释放,这些访问会直接触发野指针错误(也就是你看到的[CFString length]崩溃,本质是访问了已释放内存)。
  2. 任务取消逻辑漏洞:willSet里只是调用了ind?.cancel(),但旧的后台任务可能还在执行循环,而且你直接用新的DispatchWorkItem覆盖work[i],导致旧任务失去引用,无法追踪是否真正停止。
  3. 数据竞争风险:多个后台线程同时调用addWord修改words数组,可能导致数组内部结构损坏,进而引发崩溃。
  4. 无搜索防抖:快速输入时频繁触发搜索,导致大量后台任务被创建和取消,不仅损耗性能,还容易出现任务执行混乱的情况。

分步解决方案

1. 修复内存访问安全问题

所有实例变量的访问必须通过weak self解包后进行,避免访问已释放的对象:

func anyTranslate(with words: Words, and lang: String) {
    guard let self = self else { return } // 先确保self存在
    let array = Language.getLang(lang: lang, word: words)
    for value in array[0] ?? [""] {
        if self.test == true {
            print(true)
            return // 检测到任务取消,直接退出
        }
        if value.hasPrefix(self.searchText) {
            self.addWord(with: words)
            return
        }
    }
}

2. 完善任务取消与追踪逻辑

更新searchText的监听逻辑,确保旧任务被彻底取消,同时避免覆盖未完成的任务引用:

private var searchText = "" {
    willSet {
        // 取消所有正在运行的搜索任务
        work.forEach { $0?.cancel() }
        // 清空work数组,释放旧任务引用
        work = Array(repeating: nil, count: 5)
        test = true
    }
    didSet {
        test = false
        if searchText.count >= 2 {
            search(nil)
        } else {
            // 文本长度不足时,直接清空结果并更新UI
            DispatchQueue.main.async {
                self.words.removeAll()
                self.tableView.reloadData()
                self.noFound()
                self.tableView.isUserInteractionEnabled = true
                self.indicator.stopAnimating()
            }
        }
    }
}

同时优化search函数,确保每个后台任务能及时响应取消,并且所有任务完成后再统一更新UI:

func search(_ completion: (() -> ())?) {
    update = true
    words.removeAll()
    DispatchQueue.main.async {
        self.tableView.isUserInteractionEnabled = false
        self.indicator.isHidden = false
        self.indicator.startAnimating()
    }
    
    let arr = [ViewController.splitArr.first, ViewController.splitArr.second, ViewController.splitArr.third, ViewController.splitArr.four, ViewController.splitArr.five]
    var completedTasks = 0
    let totalTasks = arr.count
    
    for (i, value) in arr.enumerated() {
        let workItem = DispatchWorkItem { [weak self] in
            guard let self = self, !self.work[i]?.isCancelled ?? false else { return }
            let lang = Language.first()
            
            for (index, word) in value.enumerated() {
                // 每次循环前都检查任务是否被取消或test是否为true
                guard !self.work[i]?.isCancelled ?? false, !self.test else {
                    break
                }
                self.anyTranslate(with: word, and: lang)
            }
            
            // 任务完成后计数,所有任务完成后再更新UI
            DispatchQueue.main.async {
                completedTasks += 1
                if completedTasks == totalTasks {
                    if self?.words.count == 0 {
                        self?.noFound()
                    }
                    self?.tableView.reloadData()
                    self?.tableView.isUserInteractionEnabled = true
                    self?.indicator.stopAnimating()
                    completion?()
                }
            }
        }
        work[i] = workItem
        DispatchQueue.global(qos: .userInteractive).async(execute: workItem)
    }
}

3. 确保数组线程安全

给words数组添加读写锁,避免多线程同时修改导致的数据竞争:

private let wordsLock = NSLock()
private var words = [Words]()

// 修改addWord函数,加锁保护
func addWord(with word: Words) {
    wordsLock.lock()
    defer { wordsLock.unlock() } // 确保锁一定会被释放
    words.append(word)
}

4. 添加搜索防抖优化

快速输入时,延迟0.3秒再触发搜索,避免任务堆积:

private var debounceWorkItem: DispatchWorkItem?

private var searchText = "" {
    willSet {
        debounceWorkItem?.cancel()
        work.forEach { $0?.cancel() }
        work = Array(repeating: nil, count: 5)
        test = true
    }
    didSet {
        test = false
        debounceWorkItem?.cancel()
        // 创建新的防抖任务
        let workItem = DispatchWorkItem { [weak self] in
            guard let self = self else { return }
            if self.searchText.count >= 2 {
                self.search(nil)
            } else {
                DispatchQueue.main.async {
                    self.words.removeAll()
                    self.tableView.reloadData()
                    self.noFound()
                    self.tableView.isUserInteractionEnabled = true
                    self.indicator.stopAnimating()
                }
            }
        }
        debounceWorkItem = workItem
        // 延迟0.3秒执行搜索
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: workItem)
    }
}

总结

按照上面的修改,你可以解决:

  • 野指针访问导致的[CFString length]崩溃
  • 后台任务取消不彻底的问题
  • 多线程数据竞争的风险
  • 快速输入时的任务堆积问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:15:33