我正在使用领域构建词汇应用程序。我有几个词汇对象,其中包含单词列表。一个词汇表包含45000个单词
UI就是这样构建的,用户可以通过" BEGINSWITH"," CONTAINS"或" ENDSWITH"如果选择了相应的标签,则通过单词标题。
因为,有几个词汇表,有一些词汇,出现在几个词汇表中,我需要删除"重复"来自UI。
当我这样做时,对结果对象进行过滤重复,并按字母顺序对应用程序的用户界面进行排序冻结,直到过程完成。
我的问题是: 1)如果更改了选项卡,如何取消先前的过滤器和领域过滤请求(例如从包含到结束"? 2)如何在后台执行所有这些过滤/排序请求,因此UI不会冻结?
我的代码:
let vocabularyPredicate = NSPredicate(format: "enabled == 1 AND lang_from CONTAINS[c] %@", self.language.value)
self.vocabularies = Array(realm.objects(Vocabulary.self).filter(vocabularyPredicate).sorted(byKeyPath: "display_order"))
let result = List<Word>()
for object in self.vocabularies {
let predicate = NSPredicate(format: "title \(selectedFilter.value)[c] %@", self.query.value.lowercased())
result.append(objectsIn: object.words.filter(predicate))
}
self.words = Array(result).unique{$0.title}.sorted {
(s1, s2) -> Bool in return s1.title.localizedStandardCompare(s2.title) == .orderedAscending
}
selectedFilter.value被选中标签值:&#34; BEGINSWITH&#34;,&#34; CONTAINS&#34;或&#34; ENDSWITH&#34; self.query.value.lowercased() - 搜索查询。
unique {$ 0.title}是数组的扩展方法
extension Array {
func unique<T:Hashable>(map: ((Element) -> (T))) -> [Element] {
var set = Set<T>() //the unique list kept in a Set for fast retrieval
var arrayOrdered = [Element]() //keeping the unique list of elements but ordered
for value in self {
if !set.contains(map(value)) {
set.insert(map(value))
arrayOrdered.append(value)
}
}
return arrayOrdered
}
}
实际上,领域搜索速度非常快,但由于通过词汇表循环并过滤重复项+按字母顺序排序操作数组 - 请求冻结1-2秒。
更新,基于EpicPandaForce和Manuel建议:
我又潜伏了一次,看来,.distinct(by:[keypath])已经在RealmSwift新版本的结果中显示了。
我已将过滤/排序请求更改为
realm.objects(Word.self).filter(vocabularyPredicate).distinct(by: ["title"]).sorted(byKeyPath: "title", ascending: true)
更好地了解,但我想确保,通过在后台线程和UI线程之间传递对象,UI无论如何都不会冻结。我已将建议施工更新为:
DispatchQueue.global(qos: .background).async {
let realm = try! Realm()
let cachedWords = CashedWords()
let predicate = NSPredicate(format: "enabled == 1")
let results = realm.objects(Word.self).filter(predicate).distinct(by: ["title"]).sorted(byKeyPath: "title", ascending: true)
cachedWords.words.append(objectsIn: results)
try! realm.write {
realm.add(cachedWords)
}
let wordsRef = ThreadSafeReference(to: cachedWords)
DispatchQueue.main.async {
let realm = try! Realm()
guard let wordsResult = realm.resolve(wordsRef) else {
return
}
self.words = Array(wordsResult.words)
if ((self.view.window) != nil) {
self.tableView.reloadData()
}
}
print("data reload finalized")
}
答案 0 :(得分:1)
1)如果更改了标签,我如何取消之前的过滤器和域过滤请求(例如从包含到结束??
你可以创建一个NSOperation来执行任务并检查它是否在每个步骤之间被取消(fetch,check isCancelled,filter,check isCancelled,sort)。你不会立即取消它,但它可以提高你的表现。它还取决于这三个步骤中的哪一步(获取,过滤,排序)需要更长的时间......
2)如何在后台执行所有这些过滤/排序请求,因此UI不会冻结?
您可以在新的NSOperationQueue中运行该操作。 或者只使用GCD,将块分派给后台队列,在块中创建Realm实例并在那里运行代码,然后将结果分派回主队列以更新UI。 像这样:
DispatchQueue.global(qos: .userInitiated).async {
guard let realm = try? Realm() else {
return // maybe pass an empty array back to the main queue?
}
// ...
// your code here
// ...
let words = Array(result).unique{$0.title}.sorted {
(s1, s2) -> Bool in return s1.title.localizedStandardCompare(s2.title) == .orderedAscending
}
// Can't pass Realm objects directly across threads
let wordReferences = words.map { ThreadSafeReference(to: $0) }
DispatchQueue.main.async {
// Resolve references on main thread
let realm = try! Realm()
let mainThreadWords = wordReferences.flatMap { realm.resolve($0) }
// Do something with words
self.words = mainThreadWords
}
}
此外,您应该尝试优化查询:
let predicate = NSPredicate(format: "vocabulary.enabled == 1 AND vocabulary.lang_from CONTAINS[c] %@ AND title \(selectedFilter.value)[c] %@", self.language.value, self.query.value.lowercased())
let words = realm.objects(Word.self).filter(predicate).sorted(byKeyPath: "title")
let wordsReference = ThreadSafeReference(words)
// resolve this wordsReference in the main thread