Я использую Core Data для локального сохранения результатов вызова Web Services. Веб-сервис возвращает полную объектную модель, скажем, "Автомобили" - может быть около 2000 из них (и я не могу заставить веб-службу возвращать что-то меньшее, чем 1 или ВСЕ автомобили.
В следующий раз, когда я открою свое приложение, я хочу обновить сохраненную копию Core Data, снова вызвав веб-службу для всех автомобилей, однако для предотвращения дублирования мне необходимо сначала очистить все данные в локальном кэше.
Есть ли более быстрый способ очистить ВСЕ экземпляры определенного объекта в контексте управляемых объектов (например, все сущности типа "CAR" ), или мне нужно запросить их вызов, затем выполнить итерацию результатов для их удаления, затем сохраните?
В идеале я могу просто сказать, удалить все, где сущность Бла.
В iOS 9 добавлен новый класс под названием NSBatchDeleteRequest
, который позволяет легко удалять объекты, соответствующие предикату, без необходимости загружать их все в память. Вот как вы его используете:
let fetchRequest = NSFetchRequest(entityName: "Car")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try myPersistentStoreCoordinator.executeRequest(deleteRequest, withContext: myContext)
} catch let error as NSError {
// TODO: handle the error
}
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Car"];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];
NSError *deleteError = nil;
[myPersistentStoreCoordinator executeRequest:delete withContext:myContext error:&deleteError];
Более подробную информацию о пакетных удалениях можно найти в разделе "What New in Core Data" от WWDC 2015 (начиная с ~ 14: 10).
Извлечь все и удалить все:
NSFetchRequest *allCars = [[NSFetchRequest alloc] init];
[allCars setEntity:[NSEntityDescription entityForName:@"Car" inManagedObjectContext:myContext]];
[allCars setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError *error = nil;
NSArray *cars = [myContext executeFetchRequest:allCars error:&error];
[allCars release];
//error handling goes here
for (NSManagedObject *car in cars) {
[myContext deleteObject:car];
}
NSError *saveError = nil;
[myContext save:&saveError];
//more error handling here
Немного более очищен и универсален: добавьте этот метод:
- (void)deleteAllEntities:(NSString *)nameEntity
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:nameEntity];
[fetchRequest setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError *error;
NSArray *fetchedObjects = [theContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *object in fetchedObjects)
{
[theContext deleteObject:object];
}
error = nil;
[theContext save:&error];
}
Reset Сущность в Swift 3:
func resetAllRecords(in entity : String) // entity = Your_Entity_Name
{
let context = ( UIApplication.shared.delegate as! AppDelegate ).persistentContainer.viewContext
let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
do
{
try context.execute(deleteRequest)
try context.save()
}
catch
{
print ("There was an error")
}
}
try context.save()
не требуется. Спасибо мужчина!
Для Swift 2.0:
class func clearCoreData(entity:String) {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName(entity, inManagedObjectContext: moc!)
fetchRequest.includesPropertyValues = false
do {
if let results = try moc!.executeFetchRequest(fetchRequest) as? [NSManagedObject] {
for result in results {
moc!.deleteObject(result)
}
try moc!.save()
}
} catch {
LOG.debug("failed to clear core data")
}
}
Swift:
let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName(entityName, inManagedObjectContext: context)
fetchRequest.includesPropertyValues = false
var error:NSError?
if let results = context.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject] {
for result in results {
context.deleteObject(result)
}
var error:NSError?
if context.save(&error) {
// do something after save
} else if let error = error {
println(error.userInfo)
}
} else if let error = error {
println("error: \(error)")
}
Это аналогичный вопрос для здесь, и кто-то предложил установить правило удаления отношений, поэтому вам нужно удалить только один объект. Поэтому, если у вас есть или может создать сущность с отношением "многие" к автомобилям и установить правило каскадирования при удалении высшей сущности, все машины также будут удалены. Это может сэкономить время обработки, так как вам не нужно выполнять шаги, связанные с загрузкой ВСЕХ автомобилей. В большем наборе данных это может быть абсолютно необходимо.
Хороший ответ уже был отправлен, это только рекомендация!
Хорошим способом было бы просто добавить категорию к NSManagedObject
и реализовать такой метод, как я:
Файл заголовка (например, NSManagedObject+Ext.h
)
@interface NSManagedObject (Logic)
+ (void) deleteAllFromEntity:(NSString*) entityName;
@end
Файл кода: (например, NSManagedObject + Ext.m)
@implementation NSManagedObject (Logic)
+ (void) deleteAllFromEntity:(NSString *)entityName {
NSManagedObjectContext *managedObjectContext = [AppDelegate managedObjectContext];
NSFetchRequest * allRecords = [[NSFetchRequest alloc] init];
[allRecords setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext]];
[allRecords setIncludesPropertyValues:NO];
NSError * error = nil;
NSArray * result = [managedObjectContext executeFetchRequest:allRecords error:&error];
for (NSManagedObject * profile in result) {
[managedObjectContext deleteObject:profile];
}
NSError *saveError = nil;
[managedObjectContext save:&saveError];
}
@end
... единственное, что вам нужно - это получить управляемый объект ObjectContext из делегата приложения или где у вас есть его;)
после этого вы можете использовать его как:
[NSManagedObject deleteAllFromEntity:@"EntityName"];
Еще одна оптимизация может заключаться в том, что вы удаляете параметр для имени сущности и получаете имя вместо этого из clazzname. это приведет к использованию:
[ClazzName deleteAllFromEntity];
более чистый impl (как категория для NSManagedObjectContext):
@implementation NSManagedObjectContext (Logic)
- (void) deleteAllFromEntity:(NSString *)entityName {
NSFetchRequest * allRecords = [[NSFetchRequest alloc] init];
[allRecords setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:self]];
[allRecords setIncludesPropertyValues:NO];
NSError * error = nil;
NSArray * result = [self executeFetchRequest:allRecords error:&error];
for (NSManagedObject * profile in result) {
[self deleteObject:profile];
}
NSError *saveError = nil;
[self save:&saveError];
}
@end
Использование:
[managedObjectContext deleteAllFromEntity:@"EntityName"];
[AppDelegate managedObjectContext]
не обязательно является «чистой архитектурой» .. ;-)
Расширение ответа Dave Delong.
Swift Version, которая также заботится о iOS 9 и предыдущих версиях. Я также рассмотрел обработку ошибок:
let appDelegate: AppDelegate = UIApplication.sharedApplication(). делегировать как! AppDelegate
let fetchRequest = NSFetchRequest(entityName: "Car")
if #available(iOS 9.0, *) {
let delete = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try appDelegate.persistentStoreCoordinator.executeRequest(delete, withContext: appDelegate.managedObjectContext)
} catch let error as NSError {
print("Error occured while deleting: \(error)")
}
} else {
// Fallback on earlier versions
let carRequest = NSFetchRequest()
carRequest.entity = NSEntityDescription.entityForName("Cars", inManagedObjectContext: appDelegate.managedObjectContext)
carRequest.includesPropertyValues = false
do {
let cars: NSArray = try appDelegate.managedObjectContext.executeFetchRequest(carRequest)
for car in cars {
appDelegate.managedObjectContext.delete(car)
}
try appDelegate.managedObjectContext.save()
} catch let error as NSError {
print("Error occured while fetching or saving: \(error)")
}
}
Swift 3.X и Swift 4.X, простой способ. Изменить только YourTable
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "YourTable")
fetchRequest.returnsObjectsAsFaults = false
do
{
let results = try context.fetch(fetchRequest)
for managedObject in results
{
let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
context.delete(managedObjectData)
}
} catch let error as NSError {
print("Detele all my data in \(entity) error : \(error) \(error.userInfo)")
}
iOS 10 и более поздние версии
Работает со всеми версиями. Перенесите имя объекта и выполните итерацию, чтобы удалить все записи и сохранить контекст.
func deleteData(entityToFetch: String, completion: @escaping(_ returned: Bool) ->()) {
let context = NSManagedObjectContext()
context = your managedObjectContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
fetchRequest.entity = NSEntityDescription.entity(forEntityName: entityToFetch, in: context)
fetchRequest.includesPropertyValues = false
do {
let results = try context.fetch(fetchRequest) as! [NSManagedObject]
for result in results {
context.delete(result)
}
try context.save()
completion(true)
} catch {
completion(false)
print("fetch error -\(error.localizedDescription)")
}
}
CoreDataStack()
неясно, каковы ваши CoreDataStack()
или DataController()
. Обновление будет оценено;)
if #available(iOS 10.0, *)
?
Обновление Swift 4, iOS 12 и Xcode 10
100% работает только вырезать и вставлять
Просто поместите эту функцию в соответствующий класс
и вызовите эту функцию self.deleteData()
в viewDidLoad()
или в любом месте или под функцией или кнопкой, так что, щелкнув по кнопке, все данные из объекта должны быть удалены и заменить "myEntity" в качестве объекта, который вы определили в своих основных данных
func deleteData() {
let appDel:AppDelegate = (UIApplication.shared.delegate as! AppDelegate)
let context:NSManagedObjectContext = appDel.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "myEntity")
fetchRequest.returnsObjectsAsFaults = false
do
{
let results = try context.fetch(fetchRequest)
for managedObject in results
{
let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
context.delete(managedObjectData)
}
} catch let error as NSError {
print("Deleted all my data in myEntity error : \(error) \(error.userInfo)")
}
}
Почему бы не сбросить данные, полученные с помощью существующего кеша? В противном случае он не будет "обновляться", он "начинает снова", и вы можете также удалить/удалить файл SQLLite и начать все заново (при условии, что вы также не сохраняете другие данные).
Этот код будет работать как для iOS 9, так и ниже
class func deleteAllRecords(in entity : String) // entity = Your_Entity_Name
{
let context = CoreDataStack.getContext() // Note:- Replace your context here with CoreDataStack.getContext()
let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
if #available(iOS 9, *)
{
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
do
{
try context.execute(deleteRequest)
try context.save()
}
catch
{
print("There was an error:\(error)")
}
}
else
{
do{
let deleteRequest = try context.fetch(deleteFetch)
for anItem in deleteRequest {
context.delete(anItem as! NSManagedObject)
}
}
catch
{
print("There was an error:\(error)")
}
}
CoreDataStack.saveContext() // Note:- Replace your savecontext here with CoreDataStack.saveContext()
}
В Swift 3.0
func deleteAllRecords() {
//delete all data
let context = appDelegate.persistentContainer.viewContext
let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "YourClassName")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
do {
try context.execute(deleteRequest)
try context.save()
} catch {
print ("There was an error")
}
}
если объект содержит много записей, лучший способ подобен этому, поскольку он сохраняет память
- (void)deleteAll:(NSManagedObjectContext *)managedObjectContext entityName:(NSString *)entityName
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[managedObjectContext setUndoManager:nil];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setIncludesPropertyValues:NO];
[fetchRequest setFetchLimit:100]; // you can change this number if you want
NSError *error;
NSArray *items = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
while ([items count] > 0) {
@autoreleasepool {
for (NSManagedObject *item in items) {
[managedObjectContext deleteObject:item];
}
if (![managedObjectContext save:&error]) {
NSLog(@"Error deleting %@ - error:%@",self.entityName, error);
}
}
items = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
}
}
в iOS 11.3 и Swift 4.1
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest )
batchDeleteRequest.resultType = .resultTypeCount
do {
let batchDeleteResult = try dataController.viewContext.execute(batchDeleteRequest) as! NSBatchDeleteResult
print("The batch delete request has deleted \(batchDeleteResult.result!) records.")
dataController.viewContext.reset() // reset managed object context (need it for working)
} catch {
let updateError = error as NSError
print("\(updateError), \(updateError.userInfo)")
}
вы должны вызвать сброс после выполнения. Если нет, он не будет обновляться в представлении таблицы.
Swift 4, iOS 10+
Статическая функция, которая может применяться для любого объекта для удаления всех его данных
protocol NSManagedObjectHelper {
}
extension NSManagedObject: NSManagedObjectHelper {
}
extension NSManagedObjectHelper where Self: NSManagedObject {
static func removeAllObjectsInContext(_ managedContext: NSManagedObjectContext) {
let request: NSFetchRequest = Self.fetchRequest()
let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
do {
deleteRequest.resultType = .resultTypeObjectIDs//to clear objects from memory
let result = try managedContext.execute(deleteRequest) as? NSBatchDeleteResult
if let objectIDArray = result?.result as? [NSManagedObjectID] {
let changes = [NSDeletedObjectsKey : objectIDArray]
/*By calling mergeChangesFromRemoteContextSave, all of the NSManagedObjectContext instances that are referenced will be notified that the list of entities referenced with the NSManagedObjectID array have been deleted and that the objects in memory are stale. This causes the referenced NSManagedObjectContext instances to remove any objects in memory that are loaded which match the NSManagedObjectID instances in the array.*/
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [managedContext])
}
try managedContext.save()
} catch let error {
print(error)
}
}
}
"Комната" - это объект
Room.removeAllObjectsInContext(self.persistentContainer.viewContext)
Используйте NSBatchDeleteRequest для удаления нескольких записей. Если минимальная iOS равна 9.0. Если фоновый поток, выполните NSManagedObjectContext save else, используйте NSFetchRequest для получения записей и удаления всех записей в цикле и сохранения после удаления.
Решение Swift 3 с iOS 9 "NSBatchDeleteRequest" и возврат к более ранним версиям iOS, реализованным как расширение в "NSManagedObjectContext". Ссылка Apple https://developer.apple.com/library/content/featuredarticles/CoreData_Batch_Guide/BatchDeletes/BatchDeletes.html
extension NSManagedObjectContext {
func batchDeleteEntities<T: NSManagedObject>(ofType type: T.Type) throws {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: String(describing: type.self))
if #available(iOS 9.0, *) {
let request = NSBatchDeleteRequest(fetchRequest: fetchRequest)
let result = try execute(request) as? NSBatchDeleteResult
if let objectIDArray = result?.result as? [NSManagedObjectID] {
let changes = [NSDeletedObjectsKey: objectIDArray]
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [self])
}
} else {
fetchRequest.includesPropertyValues = false
let results = try fetch(fetchRequest)
if let actualResults = results as? [NSManagedObject], !actualResults.isEmpty {
actualResults.forEach { delete($0) }
}
}
}
}
iOS 9.0 и более поздние версии:
NSBatchDeleteRequest
используется для удаления записей в основных данных. Он работает очень быстро и занимает меньше времени, чтобы удалить все записи из объекта. Для аргумента требуется NSFetchRequest
. Если вы хотите удалить все записи из объекта, вы можете использовать его, и он работает для меня.
let manageObject:NSManagedObjectContext = appDelegateObject.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "EnityName")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
let persistCor:NSPersistentStoreCoordinator = appDelegateObject.persistentObject
do {
try persistCor.executeRequest(deleteRequest, withContext: manageObject)
try manageObject.save()
} catch {
print(error?.localizedDescription)
}
В Swift 2.0:
func deleteAllData(entity: String)
{
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: entity)
fetchRequest.returnsObjectsAsFaults = false
do
{
let results = try managedContext.executeFetchRequest(fetchRequest)
for managedObject in results
{
let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
managedContext.deleteObject(managedObjectData)
}
} catch let error as NSError {
print("Detele all data in \(entity) error : \(error) \(error.userInfo)")
}
}
Ответ Dave Delongs Swift 2.0 для меня грозил (в iOS 9)
Но это сработало:
let fetchRequest = NSFetchRequest(entityName: "Car")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try managedObjectContext.executeRequest(deleteRequest)
try managedObjectContext.save()
}
catch let error as NSError {
// Handle error
}
Самый быстрый способ - просто переустановить приложение на своем устройстве simulator/ios.