CoreData 未识别选择器发送给实例

3

我正在尝试将CoreData添加到我的应用程序中。此应用程序将是一个照片应用程序,并允许用户在相册中存储照片。目前,在CoreData中有两个实体,PhotoAlbum

Album 实体具有四个属性:albumCoverImageData:二进制数据id:UUIDname:字符串passwordProtected: 布尔值。除了这些属性之外,它还有一个关系photos,目标为Photo,反向为Album

Photo 实体有两个属性:id:UUIDimageData:BinaryData

当尝试将新相册添加到数据库时,在尝试保存上下文时,应用程序会崩溃,并显示以下错误。

2020-10-20 15:49:33.889808-0400 LockIt[902:92475] -[__NSConcreteUUID compare:]: unrecognized selector sent to instance 0x2817b4fe0

2020-10-20 15:53:28.604954-0400 LockIt[902:92475] [error] error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[__NSConcreteUUID compare:]: unrecognized selector sent to instance 0x2817b4fe0 with userInfo (null)

CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[__NSConcreteUUID compare:]: unrecognized selector sent to instance 0x2817b4fe0 with userInfo (null)

2020-10-20 15:53:28.622315-0400 LockIt[902:92475] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSConcreteUUID compare:]: unrecognized selector sent to instance 0x2817b4fe0'

*** First throw call stack:

(0x1937c65ac 0x1a78b442c 0x1936d0a2c 0x1937c9130 0x1937cb420 0x194b18c90 0x1998e5dc8 0x1998e58a0 0x1998e6ad0 0x1998e77b4 0x19989a410 0x199767b64 0x1998e7100 0x193725764 0x193725718 0x193724cd4 0x1937246a0 0x1949bc5f4 0x199892118 0x1998a0c50 0x199892fc0 0x199761aa0 0x104503bd8 0x104502e68 0x104502854 0x199fd2c04 0x19a2c3ec0 0x19a049990 0x19a0499b8 0x19a049990 0x19a03a1dc 0x19a088444 0x19a4d8f58 0x19a4d71a4 0x19a4d7d78 0x195c4d334 0x196177a4c 0x195c43350 0x195c43030 0x19612ef80 0x196108bc0 0x196190118 0x196193070 0x19618a5f4 0x19374381c 0x193743718 0x193742a28 0x19373cd20 0x19373c4bc 0x1aa24e820 0x1960e9164 0x1960ee840 0x1044f2178 0x193403e40)

libc++abi.dylib: terminating with uncaught exception of type NSException

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSConcreteUUID compare:]: unrecognized selector sent to instance 0x2817b4fe0'

terminating with uncaught exception of type NSException

我已经在下面附上相关文件。 数据模型中的照片实体 照片+CoreData属性

import Foundation

import CoreData





extension Photo {



    @nonobjc public class func fetchRequest() -> NSFetchRequest<Photo> {

        return NSFetchRequest<Photo>(entityName: "Photo")

    }



    @NSManaged public var id: UUID?

    @NSManaged public var imageData: Data?

    @NSManaged public var album: Album?



}



extension Photo : Identifiable {



    public var wrappedID: UUID {

        id!

    }

    

    public var wrappedImageData: Data {

        imageData!

    }

}

数据模型中的Album实体 相册+CoreData属性

import Foundation

import CoreData





extension Album {



    @nonobjc public class func fetchRequest() -> NSFetchRequest<Album> {

        return NSFetchRequest<Album>(entityName: "Album")

    }



    @NSManaged public var albumCoverImageData: Data?

    @NSManaged public var id: UUID?

    @NSManaged public var name: String?

    @NSManaged public var passwordProtected: Bool

    @NSManaged public var photos: NSSet?

    

    public var wrappedID: UUID {

        id!

    }

    

    public var wrappedAlbumCoverImageData: Data {

        albumCoverImageData!

    }

    

    public var wrappedName: String {

        name ?? "Unamed Album"

    }

    

    public var wrappedPasswordProtected: Bool {

        passwordProtected

    }

    

    public var photoArray: [Photo] {

        let set = photos as? Set<Photo> ?? []

        

        return Array(set)

    }



}



// MARK: Generated accessors for photos

extension Album {



    @objc(addPhotosObject:)

    @NSManaged public func addToPhotos(_ value: Photo)



    @objc(removePhotosObject:)

    @NSManaged public func removeFromPhotos(_ value: Photo)



    @objc(addPhotos:)

    @NSManaged public func addToPhotos(_ values: NSSet)



    @objc(removePhotos:)

    @NSManaged public func removeFromPhotos(_ values: NSSet)



}



extension Album : Identifiable {

    

}

主视图

import SwiftUI



struct AlbumCollectionView: View {

    @Environment(\.managedObjectContext) var managedObjectContext

    @FetchRequest(entity: Photo.entity(),

                  sortDescriptors: [

                    NSSortDescriptor(keyPath: \Photo.id, ascending: true)

                  ]

    ) var photos: FetchedResults<Photo>

    

    @FetchRequest(entity: Album.entity(),

                  sortDescriptors: [

                    NSSortDescriptor(keyPath: \Album.name, ascending: true)

                  ]

    ) var albums: FetchedResults<Album>

    

    @State private var showingImagePicker = false

    @State private var showingPopover = false

    @State private var inputImage: UIImage?

    @State private var albumNameInput: String = ""

    

    let columns = [

        GridItem(.adaptive(minimum: 150))

    ]

    

    var body: some View {

        ScrollView {

            LazyVGrid(columns: columns, spacing: 20) {

                ForEach(albums) { album in

                    NavigationLink(destination: AlbumView(album: album)) {

                        VStack {

                            Image(data: album.albumCoverImageData, placeholder: "No Image")

                                .resizable()

                                .aspectRatio(1, contentMode: .fill)

                                .contextMenu(menuItems: {

                                    Button(action: {

                                        deleteAlbum(selectedAlbum: album)

                                    }) {

                                        Label("Remove", systemImage: "trash")

                                    }

                                })

                            Text(album.name ?? "")

                        }

                    }

                }

            }

            .padding()

            .navigationBarTitle(Text("Albums"))

            .navigationBarItems(trailing:

                Menu {

                    Button(action: {

                        self.showingPopover = true

                    }) {

                        Label("Add Album", systemImage: "photo.on.rectangle.angled")

                    }

                    Button(action: {

                        self.showingImagePicker = true

                    }) {

                        Label("Add Photo", systemImage: "photo")

                    }

                } label: {

                    Image(systemName: "plus")

                        .popover(isPresented: $showingPopover, arrowEdge: .trailing ,content: {

                            VStack {

                                Text("New Album")

                                TextField("Enter Album Name",text: $albumNameInput)

                                    .textFieldStyle(RoundedBorderTextFieldStyle())

                                Button("Submit"){

                                    addAlbum(name: albumNameInput, albumCover: UIImage(named: "Image 1")!)

                                    albumNameInput = ""

                                    showingPopover = false

                                }

                            }

                            .padding()

                        })

                }

            )

            .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {

                ImagePicker(image: self.$inputImage)

            }

        }

    }

    

    func addAlbum(name: String, albumCover: UIImage) {

        let newAlbum = Album(context: managedObjectContext)

        

        newAlbum.id = UUID()

        newAlbum.name = name

        newAlbum.passwordProtected = false

        newAlbum.albumCoverImageData = albumCover.jpegData(compressionQuality: 1.0)

        

        saveContext()

    }

    

    func deleteAlbum(selectedAlbum: Album) {

        for album in albums {

            if selectedAlbum == album {

                self.managedObjectContext.delete(album)

            }

        }

        saveContext()

    }

    

    func loadImage() {

        guard let inputImage = inputImage else { return }

        addPhoto(image: inputImage)

    }

    

    func addPhoto(image: UIImage) {

        let newPhoto = Photo(context: managedObjectContext)

        

        newPhoto.id = UUID()

        newPhoto.imageData = image.jpegData(compressionQuality: 1.0)

        

        saveContext()

    }

    

    func saveContext() {

        do {

            try managedObjectContext.save()

        } catch {

            print("Error saving managed object context: \(error)")

        }

    }

}

我对这里发生的事情感到困惑,希望能得到任何见解!


我们不需要截图,这是你自己可以检查的。我看到了一些类似的问题,那是由于更新数据模型引起的,所以我才问。 - Joakim Danielson
我还没有改过它,但我也还没有生成新文件。在我生成文件之后,我添加了“wrapped”属性,但这些是我所做的唯一更改。 - JoshHolme
离题了,但是最好将自己的计算属性和其他东西添加到实体类的扩展中,在一个_单独的_文件中。 - Joakim Danielson
注意,一旦我能够缩小导致此错误的原因,我将研究更改它们。 - JoshHolme
@Cora,从相册到照片的关系是一对多。我不确定为什么passwordProtected不是可选的。我没有特别设置任何内容,但如果我必须猜测,那可能是因为它是一个布尔值?然而,这可能是一个糟糕的猜测,也可能是错误的。 - JoshHolme
显示剩余7条评论
1个回答

7
你提到问题出现在添加和保存新的Album时,但我认为问题实际上与Photos有关。错误信息如下:

reason: '-[__NSConcreteUUID compare:]: unrecognized selector sent to instance

这里使用了旧版Objective-C术语(CoreData的核心是基于Objective-C的),它表示存在一个类型为__NSConcreteUUID 的对象(这可能是CoreData用于存储UUID的对象类型),而CoreData正在尝试调用该对象的compare方法。当对对象进行排序时,会调用compare方法:比较对象A和对象B以确定它们相对于彼此的顺序。
因此,问题的根源在于你的视图正在按照UUID对某些内容进行排序,而这似乎是指Photos
     @FetchRequest(entity: Photo.entity(),
         sortDescriptors: [
             NSSortDescriptor(keyPath: \Photo.id, ascending: true)
             ]
         ) var photos: FetchedResults<Photo>

但UUID不能以这种方式进行排序。除非有很好的理由需要按UUID排序,否则我会改变排序顺序并使用不同的属性。


1
谢谢!您是否知道有哪些文档可以解释为什么UUID不能以这种方式排序,以及哪些键不能用于排序的一般规则是什么?拥有一个具有UUID的实体然后按此ID进行排序以使顺序保持一致感觉很自然。 - cgold

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接