Я просто хочу поделиться тем, что у меня есть приложение Core Data со следующими функциями:
Каждый из них представляет собой кнопку, которую пользователь может нажать, чтобы отфильтровать данные, например (слева направо): «Все», «Входящие», «Архив», «Еще», «Хорошо», «Зло», «Важно». Вы также заметите, что каждая кнопка имеет счетчик.
В моей ViewModel у меня есть функция для подсчета каждого из них, которая вызывается, когда в приложении onAppear()
func countItem() -> (countTotal: Int, countArchive: Int, countGood: Int, countEvil: Int, countImportant: Int) {
let request: NSFetchRequest<ThoughtEntity> = ThoughtEntity.fetchRequest()
request.predicate = NSPredicate(format: "thoughts != %@", "")
let items1 = (try? manager.container.viewContext.fetch(request)) ?? []
let countTotalPredicate = items1.count
request.predicate = NSPredicate(format: "isArchive == true")
let items2 = (try? manager.container.viewContext.fetch(request)) ?? []
let countArchivePredicate = items2.count
request.predicate = NSPredicate(format: "type CONTAINS %@", "good")
let items3 = (try? manager.container.viewContext.fetch(request)) ?? []
let countGoodPredicate = items3.count
request.predicate = NSPredicate(format: "type CONTAINS %@", "evil")
let items4 = (try? manager.container.viewContext.fetch(request)) ?? []
let countEvilPredicate = items4.count
request.predicate = NSPredicate(format: "type CONTAINS %@", "important")
let items5 = (try? manager.container.viewContext.fetch(request)) ?? []
let countImportantPredicate = items5.count
return (countTotalPredicate, countArchivePredicate, countGoodPredicate, countEvilPredicate, countImportantPredicate)
}
Код работает достаточно хорошо.
Мне интересно, есть ли лучший способ сделать это вместо того, чтобы получать данные из базы данных 5 раз? Возможно, «стоимость» извлечения невелика, так что это не имеет значения, но мне интересно, есть ли лучший и более эффективный способ сделать это?
Вы можете использовать метод count(for:)
в viewContext, чтобы вернуть только количество, а не все объекты.
let request: NSFetchRequest<ThoughtEntity> = ThoughtEntity.fetchRequest()
request.predicate = NSPredicate(format: "thoughts != %@", "")
let countTotalPredicate = try? manager.container.viewContext.count(for: request)) ?? 0
Что ж, вам действительно нужно пять разных счетчиков с пятью разными предикатами, если я не правильно понимаю ваши требования. Использование метода подсчета заставляет Core Data выполнять меньше тяжелой работы и требует на пять строк кода меньше.
Да, вы правильно понимаете мое требование. Спасибо.
Такой подход избавляет от необходимости создавать экземпляры всех этих объектов только для их подсчета. Если ваши предикаты возвращают много объектов, экономия времени и памяти может быть существенной.
Вы можете использовать NSExpression и метод подсчета для трех предикатов, используя атрибут type
, но я думаю, что вам нужно сделать два других отдельно.
Что-то вроде этого
let request = NSFetchRequest<NSDictionary>(entityName: "ThoughtEntity")
request.sortDescriptors = [NSSortDescriptor(keyPath: \ThoughtEntity.type, ascending: false)]
let predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [
NSPredicate(format: "type CONTAINS %@", "good"),
NSPredicate(format: "type CONTAINS %@", "evil"),
NSPredicate(format: "type CONTAINS %@", "important")
]
request.predicate = predicate
let keyPath = NSExpression(forKeyPath: "type")
let expression = NSExpression(forFunction: "count:", arguments: [keyPath])
let countDescription = NSExpressionDescription()
countDescription.expression = expression
countDescription.name = "count"
countDescription.expressionResultType = .integer64AttributeType
request.propertiesToGroupBy = ["type"]
request.propertiesToFetch = ["type", countDescription]
request.resultType = .dictionaryResultType
Однако обратите внимание: если объект не содержит большого количества объектов, возможно, будет быстрее получить все за один запрос, а затем выполнить подсчет в памяти, поскольку даже с этим решением вам потребуется выполнить 3 запроса.
Возможно, предикат можно улучшить, чтобы использовать что-то вроде sql IN (...)
Я ценю ваш ответ, Йоаким. Хотя я собираюсь следовать ответу Магнаса, потому что я понимаю код, я также изучу ваш ответ для своего обучения. Спасибо.
Нет проблем, другой ответ более простой, но я все же хотел показать эту альтернативу.
Спасибо. Это хороший совет. Но мне все равно нужно сделать это 5 раз, верно?