Выборка объектов в CoreData, вызывающая сбой - PullRequest
0 голосов
/ 29 августа 2018

У меня есть модель, которая выглядит следующим образом и содержит свойства NSManagedObject, а именно свойство blendsWith, которое имеет тип [Tag]:

extension Oil {

@nonobjc public class func fetchRequest() -> NSFetchRequest<Oil> {
    return NSFetchRequest<Oil>(entityName: "Oil")
}

@NSManaged public var blendsWith: [Tag]?
@NSManaged public var color: String?
@NSManaged public var commentsCount: Int64
@NSManaged public var id: Int64
@NSManaged public var imageURL: String?
@NSManaged public var latinName: String?
@NSManaged public var name: String?
@NSManaged public var properties: NSObject?
@NSManaged public var research: String?
@NSManaged public var resourceType: String?
@NSManaged public var viewsCount: Int64

}

public class Oil: NSManagedObject, Codable {

enum CodingKeys: String, CodingKey {
    case resourceType = "resource_type"
    case id, name
    case imageURL = "image_url"
    case color
    case latinName = "latin_name"
    case emotions
    case safetyInformation = "safety_information"
    case fact, research
    case viewsCount = "views_count"
    case commentsCount = "comments_count"
    case blendsWith = "blends_with"
    case foundInBlends = "found_in_blends"
    case properties
    case sourcingMethods = "sourcing_methods"
    case usages
}

required convenience public init(from decoder: Decoder) throws {
    let context = CoreDataHelper.sharedInstance.persistentContainer.viewContext
    guard let entity = NSEntityDescription.entity(forEntityName: "Oil", in: context) else { fatalError() }

    self.init(entity: entity, insertInto: context)

    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.resourceType = try! container.decodeIfPresent(String.self, forKey: .resourceType)!
    self.id = try! container.decodeIfPresent(Int64.self, forKey: .id)!
    self.name = try! container.decodeIfPresent(String.self, forKey: .name)!
    self.imageURL = try! container.decodeIfPresent(String.self, forKey: .imageURL)!
    self.color = try! container.decodeIfPresent(String.self, forKey: .color)!
    self.viewsCount = try! container.decodeIfPresent(Int64.self, forKey: .viewsCount)!
    self.viewsCount = try! container.decodeIfPresent(Int64.self, forKey: .viewsCount)!
    self.commentsCount = try! container.decodeIfPresent(Int64.self, forKey: .commentsCount)!
    self.latinName = try! container.decodeIfPresent(String.self, forKey: .latinName)!

    if let blendsWith = try container.decodeIfPresent([Tag].self, forKey: CodingKeys.blendsWith) {
        self.blendsWith = blendsWith
    }
}

public func encode(to encoder: Encoder) throws {

}

}

Tag выглядит так:

extension Tag {

@nonobjc public class func fetchRequest() -> NSFetchRequest<Tag> {
    return NSFetchRequest<Tag>(entityName: "Tag")
}

@NSManaged public var id: Int64
@NSManaged public var name: String?
@NSManaged public var resourceType: String?
@NSManaged public var tagType: String?
@NSManaged public var viewsCount: Int64

}

public class Tag: NSManagedObject, Codable {

enum CodingKeys: String, CodingKey {
    case resourceType = "resource_type"
    case id, name
    case viewsCount = "views_count"
    case tagType = "tag_type"
}

required convenience public init(from decoder: Decoder) throws {
    let context = CoreDataHelper.sharedInstance.persistentContainer.viewContext
    guard let entity = NSEntityDescription.entity(forEntityName: "Tag", in: context) else { fatalError() }

    self.init(entity: entity, insertInto: context)

    let container = try decoder.container(keyedBy: CodingKeys.self)

    self.resourceType = try! container.decodeIfPresent(String.self, forKey: .resourceType)!
    self.id = try! container.decodeIfPresent(Int64.self, forKey: .id)!
    self.name = try! container.decodeIfPresent(String.self, forKey: .name)!
    if let viewsCount = try container.decodeIfPresent(Int64.self, forKey: .viewsCount) {
        self.viewsCount = viewsCount
    } else  {
        self.viewsCount = 0
    }
    if let tagType = try container.decodeIfPresent(String.self, forKey: .tagType) {
        self.tagType = tagType
    } else {
        self.tagType = "lol"
    }
}

public func encode(to encoder: Encoder) throws {

}

}

Когда я собираюсь получить данные Oil, хранящиеся локально, я получаю этот сбой:

2018-08-29 20:31:30.602764+0100 EL[27994:14799374] -[EL.Tag initWithCoder:]: unrecognized selector sent to instance 0x60c000679980
2018-08-29 20:31:30.603905+0100 EL[27994:14799374] [error] error: exception handling request: <NSSQLFetchRequestContext: 0x608000181ee0> , -[EL.Tag initWithCoder:]: unrecognized selector sent to instance 0x60c000679980 with userInfo of (null)
CoreData: error: exception handling request: <NSSQLFetchRequestContext: 0x608000181ee0> , -[EL.Tag initWithCoder:]: unrecognized selector sent to instance 0x60c000679980 with userInfo of (null)
2018-08-29 20:31:30.612185+0100 EL[27994:14799374] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[EL initWithCoder:]: unrecognized selector sent to instance 0x60c000679980'

Что может быть причиной этого сбоя?

Для справки, мой метод извлечения выглядит следующим образом:

func getItems<T : NSManagedObject>(predicate : NSPredicate? = nil) -> [T]{
        do {
            let reqest = T.fetchRequest()
            reqest.predicate = predicate
            if let items = try persistentContainer.viewContext.fetch(reqest) as? [T] {
                return items
            } else {
                return [T]()
            }
        } catch let error as NSError {
            print("Could not fetch. \(error), \(error.userInfo)")
            return [T]()
        }
    }

И работает так:

let arrat : [Oil] = CoreDataHelper.sharedInstance.getItems()

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

A Transformable обычно кодируется CoreData в Data (сохраняется именно так), а кодирование NSManagedObject может делать то, чего вы не ожидаете. Расшифровка может сделать вещи «более» неожиданными. (Кодирование выполняется автоматически NSKeyedArchiver и NSKeyedUnarchiver при назначении / использовании.)

Если вы хотите использовать Transformable, то делать это NSManagedObject бессмысленно из-за аргумента выше. (У меня, по крайней мере, нет опыта работы с ним, и я не слышал, что он будет использовать.)

Так что обычно Transformable ИЛИ CoreData-relationshipsNSManagedObject с) моделировать одно отношение в CoreData, а не оба - моделировать одно.

0 голосов
/ 29 августа 2018

Если вы отлаживаете этот код, он входит в функцию Tag init? Похоже, что компилятор не видит вашу функцию Tag.init, что, скорее всего, связано с тем, что ваша модель данных для объекта Oil неправильно устанавливает тип класса свойства blendsWith.

Проверьте свою модель данных для масла и убедитесь, что для blendsWith указан правильный тип метки.

EDIT: Чтобы базовые данные могли подобрать настройки вашего класса, вам может потребоваться добавить флаг objc перед определением класса:

@objc(Tag)
public class Tag ...
...