iOS Swift中CoreData图片删除失败求助:无报错但数据未删除
核心问题:CoreData删除图片操作无效(无报错但未真正删除)
我的CoreData包含Photos和Folder两个实体,Folder与Photos是一对多关系(一个文件夹可包含多张图片)。Photos的image属性为Binary Data类型,Folder的folderName属性为String类型。目前图片的保存与查询功能正常,但删除图片操作无报错却未真正删除CoreData中的数据。
DataManager类代码
class DataManager { static let shared = DataManager() lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "CoreDataRelationship") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) return container }() func folder(name: String) -> Folder { let folder = Folder(context: persistentContainer.viewContext) folder.folder_name = name return folder } func photo(image: [UIImage], folder: Folder) -> Photos { let photo = Photos(context: persistentContainer.viewContext) let myImagesDataArray = convertImageToData(myImagesArray: image) //to store array of images using encoding var images: Data? do { images = try NSKeyedArchiver.archivedData(withRootObject: myImagesDataArray, requiringSecureCoding: true) } catch { print("error") } photo.setValue(images, forKeyPath: "image") folder.addToPhotos(photo) return photo } func folders() -> [Folder] { let request: NSFetchRequest<Folder> = Folder.fetchRequest() var fetchedFolders: [Folder] = [] do { fetchedFolders = try persistentContainer.viewContext.fetch(request) } catch let error { print("Error fetching folders \(error)") } return fetchedFolders } func photos(folder: Folder) -> [UIImage] { var imageDataArray = [Data]() var myImagesdataArray = [Data]() let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Photos") fetchRequest.predicate = NSPredicate(format: "folder = %@", folder) do { let result = try persistentContainer.viewContext.fetch(fetchRequest) for data in result { imageDataArray.append(data.value(forKey: "image") as! Data) } } catch let error as NSError { print("Could not fetch. \(error), \(error.userInfo)") } //fetch image using decoding imageDataArray.forEach { (imageData) in var dataArray = [Data]() dataArray = unarchiveDocument(unarchivedObject: imageData)//try NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: imageData) as! [Data] myImagesdataArray.append(contentsOf: dataArray) } let imagesArray = convertDataToImages(imageDataArray: myImagesdataArray) return imagesArray } //Core Data Saving support func save (){ let context = persistentContainer.viewContext if context.hasChanges { do { try context.save() } catch { let nserror = error as NSError fatalError("Unresolved error \(nserror), \(nserror.userInfo)") } } } func deletePhoto(photo: Photos) { let context = persistentContainer.viewContext context.delete(photo) save() } func deleteFolder(folder: Folder) { let context = persistentContainer.viewContext context.delete(folder) save() } func convertImageToData(myImagesArray: [UIImage]) -> [Data] { var myImagesDataArray = [Data]() myImagesArray.forEach({ (image) in myImagesDataArray.append(image.pngData()!) }) return myImagesDataArray } func convertDataToImages(imageDataArray: [Data]) -> [UIImage] { var myImagesArray = [UIImage]() imageDataArray.forEach { (imageData) in myImagesArray.append(UIImage(data: imageData)!) } return myImagesArray } func unarchiveDocument(unarchivedObject:Data) -> [Data] { do { if let document = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(unarchivedObject) as? [Data] { return document } } catch { // do something with the error } return [] } }
删除操作代码
@objc func deleteTapped(_ sender: UIBarButtonItem) { var deleteNeededIndexPath: [IndexPath] = [] for(key,value) in dictionarySelctedIndexPath { if value { deleteNeededIndexPath.append(key) } } for i in deleteNeededIndexPath { let photo = photos[i.item] photoTobeDelete.append(photo) } var deletedPhoto = DataManager.shared.photo(image: photoTobeDelete, folder: folder!) DataManager.shared.deletePhoto(photo: deletedPhoto) collectionView.deleteItems(at: deleteNeededIndexPath) dictionarySelctedIndexPath.removeAll() }
问题根源与修复方案
问题核心
你在删除逻辑中犯了致命错误:没有删除CoreData中已存在的Photos实体,而是新建了一个完全无关的Photos实例去执行删除。
看这段错误代码:
var deletedPhoto = DataManager.shared.photo(image: photoTobeDelete, folder: folder!) DataManager.shared.deletePhoto(photo: deletedPhoto)
调用photo(image:folder:)会在CoreContext中创建全新的Photos对象,你删除的是这个新对象,而非CoreData中原本存储的、对应选中图片的实体。
修复步骤
- 新增获取
Photos实体的方法
在DataManager中添加方法,直接返回CoreData中的Photos实体引用,而非仅返回UIImage:
func fetchPhotoEntities(for folder: Folder) -> [Photos] { let fetchRequest: NSFetchRequest<Photos> = Photos.fetchRequest() fetchRequest.predicate = NSPredicate(format: "folder = %@", folder) do { return try persistentContainer.viewContext.fetch(fetchRequest) } catch let error { print("Error fetching photo entities: \(error)") return [] } }
- 在视图控制器中保存实体引用
新增数组存储Photos实体,确保和UI展示的图片一一对应:
// 视图控制器中新增属性 var photoEntities: [Photos] = [] // 加载数据时同时获取实体 photoEntities = DataManager.shared.fetchPhotoEntities(for: folder!) photos = DataManager.shared.photos(folder: folder!)
- 修正删除逻辑
直接从photoEntities中获取要删除的CoreData实体,执行删除:
@objc func deleteTapped(_ sender: UIBarButtonItem) { var deleteNeededIndexPath: [IndexPath] = [] for(key,value) in dictionarySelctedIndexPath { if value { deleteNeededIndexPath.append(key) } } // 按倒序删除,避免索引错乱 for indexPath in deleteNeededIndexPath.sorted(by: { $0.item > $1.item }) { let targetEntity = photoEntities[indexPath.item] DataManager.shared.deletePhoto(photo: targetEntity) // 同步更新本地数组 photoEntities.remove(at: indexPath.item) photos.remove(at: indexPath.item) } collectionView.deleteItems(at: deleteNeededIndexPath) dictionarySelctedIndexPath.removeAll() }
额外优化建议
- 替换
setValue(forKeyPath:)为直接属性访问:photo.image = images,提升代码安全性和可读性。 - 处理
pngData()可能返回nil的情况,避免强制解包导致崩溃。 - 删除操作后可重新拉取数据,确保UI与CoreData状态完全同步。
内容的提问来源于stack exchange,提问作者Asim Iftikhar Abbasi
相关产品推荐
相关产品推荐

