我正在使用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
意味着我“向已经发布的对象发送消息”。任何人都可以帮我理解如何解决这个问题吗?
更新:在
下添加了searchAllTablesstatic 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)
}
答案 0 :(得分:0)
ManagedObjectContexts
和managedObjects
不是线程安全的。无论是阅读还是写作。我不需要在searchAllTables
中看到你在做什么就知道你做错了。有两种类型的上下文 - 主队列上下文(必须始终从主线程访问)和私有队列上下文,只能通过performBlock进行评估。在全局后台线程上运行没有正确的上下文。
在我看来,您的问题是您正在将所有对象加载到内存中并测试每个对象,而不是在获取请求上使用NSPredicate。我建议在主线程上运行搜索并修复它,这样就不会花这么长时间。