【问题标题】:Search CoreData background thread causes EXC_BAD_ACCESS搜索CoreData后台线程导致EXC_BAD_ACCESS
【发布时间】:2018-06-12 12:09:13
【问题描述】:

我正在使用 Swift 4、iOS 11

我显然想在这里做一些超出我知识水平的事情,但我希望有人能提供帮助。

我正在实现从 CoreData 获取结果的表视图搜索。搜索变得太慢了,所以我现在尝试异步运行它以避免 UI 滞后。

在我的主 VC(表格视图)上,我有以下在 textDidChange 上运行的逻辑:

DispatchQueue.global(qos: .background).async {

    DataManager.searchAllTables(searchText: searchText){  (returnResults) -> () in

        DispatchQueue.main.async {

            // LOGIC TO GIVE RETURNED RESULTS TO TABLE

            self.resultsTable.reloadData()
        }
    }
}

在函数 searchAllTables 的 DataManager 中,我在每个 CoreData 关系上运行以下逻辑:

// Get Disease
if let disease = bug.related_disease?.allObjects {
    if !disease.isEmpty {
        if disease.contains(where: { (($0 as AnyObject).name?.lowercased().contains(searchTerm.lowercased()))! || (($0 as AnyObject).name?.lowercased().contains(self.convertAcronyms(searchTerm: searchTerm)))! }){
            // LOGIC TO DETERMINE POINTS
        }
    }
}

在尝试使其异步之前它运行良好,但现在它给了我错误

线程 5:EXC_BAD_ACCESS(代码=1,地址=0x1a1b40cc771)

代码行

if let disease = bug.related_disease?.allObjects {

如果用户键入缓慢,则不会出现此错误。这让我觉得后台线程正在交叉,并且我试图从 CoreData 获取的对象发生了一些事情。

我知道EXC_BAD_ACCESS 表示我正在“向已释放的对象发送消息”。谁能帮我理解如何解决这个问题?

更新:在下面添加了 searchAllTables

static func searchAllTables(searchText:String, completion: @escaping ((_ returnResults:[BugElement])->())) {

    var bugElements:[BugElement] = []
    var bugElementShell:[BugElement] = []

    var returnValueRank:Int = 0
    var categoryRestriction:String = "all" // Search all categories by default
    var numSearchTerms:Int = searchText.components(separatedBy: " ").count
    let searchTerm:String = self.getSearchPhrases(searchTerms: searchText.components(separatedBy: " ")).1
    numSearchTerms = self.getSearchPhrases(searchTerms: searchText.components(separatedBy: " ")).0

    // Set category restriciton if needed
    if self.bugFilter.category_restricted[searchTerm.lowercased()] != nil{
        categoryRestriction = self.bugFilter.category_restricted[searchTerm.lowercased()]!
    }

    let fetchRequest: NSFetchRequest<Bugs> = Bugs.fetchRequest()

    // DOES THIS HELP SPEED?
    if searchTerm.count <= 3{
        let predicate = NSPredicate(format:"name beginswith[c] %@ OR ANY related_disease.name contains[c] %@ OR ANY related_general.name beginswith[c] %@ OR ANY related_gramstain.name beginswith[c] %@ OR ANY related_keypoints.name beginswith[c] %@ OR ANY related_laboratory.name beginswith[c] %@ OR ANY related_morphology.name beginswith[c] %@ OR ANY related_prevention.name beginswith[c] %@ OR ANY related_signs.name beginswith[c] %@ OR ANY related_treatments.name beginswith[c] %@ OR ANY related_type.name beginswith[c] %@", argumentArray: [searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased()])
        fetchRequest.predicate = predicate
    } else{
        let predicate = NSPredicate(format:"name contains[c] %@ OR related_disease.name contains[c] %@ OR related_general.name contains[c] %@ OR related_gramstain.name contains[c] %@ OR related_keypoints.name contains[c] %@ OR related_laboratory.name contains[c] %@ OR related_morphology.name contains[c] %@ OR related_prevention.name contains[c] %@ OR related_signs.name contains[c] %@ OR related_treatments.name contains[c] %@ OR related_type.name contains[c] %@", argumentArray: [searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased(),searchTerm.lowercased()])
        fetchRequest.predicate = predicate
    }

    do {
        let diseases = try DataManager.context.fetch(fetchRequest)

        for bug in diseases{

            // Points system (all matches past array are worth 1)
            var matchValue_name:[Int] = [10, 8, 4]
            var matchValue_disease:[Int] = [8, 4, 3]
            var matchValue_general:[Int] = [5, 3, 2]
            var matchValue_gramstain:[Int] = [5, 3, 2]
            var matchValue_keypoints:[Int] = [5, 3, 2]
            var matchValue_laboratory:[Int] = [2]
            var matchValue_morphology:[Int] = [5, 3, 2]
            var matchValue_prevention:[Int] = [2]
            var matchValue_signs:[Int] = [1]
            var matchValue_treatment:[Int] = [5, 3, 2]
            var matchValue_type:[Int] = [1]

            // Break down by word
            var matchedNameTerms:Int = 0
            var matchedDiseaseTerms:Int = 0
            var matchedGeneralTerms:Int = 0
            var matchedGramStainTerms:Int = 0
            var matchedKeyPointsTerms:Int = 0
            var matchedLaboratoryTerms:Int = 0
            var matchedMorphologyTerms:Int = 0
            var matchedPreventionTerms:Int = 0
            var matchedSignsTerms:Int = 0
            var matchedTreatmentTerms:Int = 0
            var matchedTypeTerms:Int = 0


            // BEGIN: By term

            var matchBasis = Set<String>()

            if categoryRestriction == "all" || categoryRestriction == "name"{
                // FAULT

                if bug.name.lowercased().contains(searchTerm.lowercased()){
                    matchedNameTerms += 1

                    // Matched based on disease name
                    if matchedNameTerms > (matchValue_name.count-1){
                        // Match beyond point assignment
                        returnValueRank += 1
                    } else {
                        // Matched within point assignment, add those points
                        returnValueRank += matchValue_name[(matchedNameTerms-1)]
                    }

                    // Append name if first match
                    if matchedNameTerms == 1{
                        matchBasis.insert("Name")
                    }

                }
            }

            // Get Disease
            if categoryRestriction == "all" || categoryRestriction == "disease"{
                // FAULT

                if let disease = bug.related_disease?.allObjects {
                    if !disease.isEmpty {
                        if disease.contains(where: { (($0 as AnyObject).name?.lowercased().contains(searchTerm.lowercased()))! || (($0 as AnyObject).name?.lowercased().contains(self.convertAcronyms(searchTerm: searchTerm)))! }){
                            matchedDiseaseTerms += 1

                            // Matched based on Disease
                            if matchedDiseaseTerms > (matchValue_disease.count-1){
                                // Match beyond point assignment
                                returnValueRank += 1
                            } else {
                                // Matched within point assignment, add those points
                                returnValueRank += matchValue_disease[(matchedDiseaseTerms-1)]
                            }

                            // Append Disease if first match
                            if matchedDiseaseTerms == 1{
                                matchBasis.insert("Disease")
                            }
                        }
                    }
                }
            }

            // ADDITIONAL MATCHES JUST LIKE ABOVE DISEASE BLOCK

            // END: By term

            if (matchedNameTerms + matchedDiseaseTerms + matchedGeneralTerms + matchedGramStainTerms + matchedKeyPointsTerms + matchedLaboratoryTerms + matchedMorphologyTerms + matchedPreventionTerms + matchedSignsTerms + matchedTreatmentTerms + matchedTypeTerms) > 0{

                // Create Element
                let bugElement = BugElement(rank: returnValueRank, name: bug.name, match: matchBasis) // Initialize struct
                bugElementShell.append(bugElement)


            }

            returnValueRank = 0

        }

    } catch {
        if DataManager.debug{ print("Could not get Bugs!") }
    }


    // Handle stored search
    if numSearchTerms == 0{
        // No stored search
        //print("None")
        self.storedBugElements.append(bugElementShell)
    } else if numSearchTerms > self.storedBugElements.count{
        // New search term
        //print("New")
        self.storedBugElements.append(bugElementShell)
    } else if numSearchTerms < self.storedBugElements.count{
        // Deleted search term
        //print("Delete")
        self.storedBugElements.removeLast()
    } else if numSearchTerms == self.storedBugElements.count{
        // Still typing search term
        //print("Still...")
        self.storedBugElements.removeLast()
        self.storedBugElements.append(bugElementShell)
    }

    // Handle stored search
    if self.storedBugElements.count > 0 {
        let namedElements = self.storedBugElements.joined().map { ($0.name, $0) }
        // Now combine them as you describe. Add the ranks, and merge the items
        let uniqueElements =
            Dictionary<String, BugElement>(namedElements,
                                           uniquingKeysWith: { (lhs, rhs) -> BugElement in
                                                let sum = lhs.rank + rhs.rank
                                                return BugElement(rank: sum,
                                                           name: lhs.name,
                                                           match: lhs.match.union(rhs.match))
            })

        // The result is the values of the dictionary
        let result = uniqueElements.values
        bugElements = result.sorted { $0.rank > $1.rank }

    } else{
        bugElements = bugElementShell.sorted { $0.rank > $1.rank }
    }



    completion(bugElements)

}

【问题讨论】:

  • 我们需要看看你如何实现searchAllTables
  • 谢谢罗格!我很快就会用我的 searchAllTables 代码更新帖子。

标签: core-data ios11 swift4 xcode9


【解决方案1】:

ManagedObjectContextsmanagedObjects 不是线程安全的。既不是为了阅读,也不是为了写作。我不需要在searchAllTables 中查看您在做什么,就知道您做错了。有两种类型的上下文 - 主队列上下文(必须始终从主线程访问)和只能通过 performBlock 评估的私有队列上下文。没有正确的上下文可以在全局后台线程上运行。

在我看来,您的问题是您将所有对象加载到内存中并测试每个对象,而不是在获取请求上使用 NSPredicate。我建议在主线程上运行搜索并修复它,这样就不会花费这么长时间。

【讨论】:

  • 我认为这是我搞砸的一种方式,谢谢。我正在使用谓词,但我有一个复杂的搜索算法,它根据匹配的关系将点分配给“错误”。最后,它会计算分数以确定哪个错误应该排在第一位。这就是为什么它很慢以及为什么我不认为我可以只使用谓词的原因。不过,我可能错了……
  • 如果您没有可以排序的字段,则在实体更新时创建并更新它。
  • 谢谢乔恩。目前,我正在动态创建 rank:Int 并按此排序。我可能能够更多地依赖谓词而不是排名,这可能会加快应用程序的速度。为了清楚起见,您是否建议我在 CoreData 中创建一个按排序的字段?如果是这样,那么鉴于用户的搜索每次都会有所不同,我不清楚它是如何工作的。当有人键入“gr”时,它会匹配许多结果,因此仅按字母顺序(例如)排序并不理想,因为某些“gr”比其他的更重要。
猜你喜欢
  • 2014-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多