搜索CoreData后台线程会导致EXC_BAD_ACCESS

时间:2018-01-02 19:41:12

标签: core-data ios11 swift4 xcode9

我正在使用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)

}

1 个答案:

答案 0 :(得分:0)

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

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