我使用Core Data在本地持久化Web服务调用的结果。web服务返回完整的对象模型,比如说,“Cars”——可能有2000辆车(我不能让web服务返回小于1辆或ALL辆的任何东西。

下次打开应用程序时,我希望通过再次调用所有汽车的Web服务来刷新Core Data持久化副本,但是为了防止重复,我需要首先清除本地缓存中的所有数据。

是否有一种更快的方法来清除管理对象上下文中特定实体的所有实例(例如,类型为“CAR”的所有实体),或者我需要查询它们调用,然后遍历结果删除每个,然后保存?

理想情况下,我可以说删除实体所在的所有地方等等。


当前回答

如果实体包含很多条目,最好的方法是这样,因为它节省内存

 - (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];
    }
}

其他回答

这是一个类似的问题,有人建议设置一个关系删除规则,这样你只需要删除一个对象。因此,如果您已经或可以创建一个与汽车具有多对关系的实体,并将删除规则设置为级联,当您删除更高的实体时,所有的汽车也将被删除。这可以节省一些处理时间,因为您不必执行加载所有汽车的步骤。在更大的数据集中,这可能是绝对必要的。

NSBatchDeleteRequest Swift 5.5和Xcode 13.2

删除SQLite持久存储中的对象,而不加载到内存中。在持久存储上执行的更改不会反映在当前内存中的对象中。

执行批量删除之后,从持久存储中删除内存中的所有对象。

下面是执行batchDeleteRequest的一些方便的扩展方法

extension NSManagedObject {
    
    private static var entityName: String {
        return String(describing: self)
    }
    
    static func fetchRequest<Self>(
        with predicate: NSPredicate? = nil,
        configureBlock: ((NSFetchRequest<Self>) -> Void)? = nil
    ) -> NSFetchRequest<Self> where Self: NSFetchRequestResult {
        let request = NSFetchRequest<Self>(entityName: entityName)
        request.predicate = predicate
        configureBlock?(request)
        return request
    }
    
    static func batchDelete(with fetchRequest: NSFetchRequest<NSFetchRequestResult>,
                            in context: NSManagedObjectContext) {
        let batchDeteleRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
        batchDeteleRequest.resultType = .resultTypeObjectIDs
        do {
            if let fetchResult = try context.execute(batchDeteleRequest) as? NSBatchDeleteResult,
               let deletedManagedObjectIds = fetchResult.result as? [NSManagedObjectID], !deletedManagedObjectIds.isEmpty {
                let changes = [NSDeletedObjectsKey: deletedManagedObjectIds]
                NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [context])
            }
        } catch {
            print("Error while executing batchDeleteRequest: \(error.localizedDescription)")
        }
    }
}

示例:我们可以通过传递fetchRequest调用batchDelete静态方法批量删除实体

class Entity: NSManagedObject {
    @NSManaged var name: String?
    @NSManaged var value: String?
}

extension Entity {
    
    static func executeBatchDelete(in context: NSManagedObjectContext) {
        let predicate = NSPredicate(format: "\(#keyPath(Entity.value)) == %@)", "abc")
        let deleteRequest: NSFetchRequest<NSFetchRequestResult> = Entity.fetchRequest(with: predicate)
        Entity.batchDelete(with: deleteRequest, in: context)
    }
}

如果实体包含很多条目,最好的方法是这样,因为它节省内存

 - (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 9及以上版本:

iOS 9添加了一个名为NSBatchDeleteRequest的新类,允许您轻松删除匹配谓词的对象,而无需将它们全部加载到内存中。下面是你如何使用它:

斯威夫特5

let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Car")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

do {
    try myPersistentStoreCoordinator.execute(deleteRequest, with: myContext)
} catch let error as NSError {
    // TODO: handle the error
}

objective - c

NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Car"];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];

NSError *deleteError = nil;
[myPersistentStoreCoordinator executeRequest:delete withContext:myContext error:&deleteError];

关于批量删除的更多信息可以在WWDC 2015的“Core Data新内容”会议中找到(开始时间约为14:10)。

iOS 8及更早版本:

获取并删除它们:

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

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)
    }