CoreData 和 Codable 类编译器错误:在从初始化程序返回之前,未在所有路径上调用“self.init”

Posted

技术标签:

【中文标题】CoreData 和 Codable 类编译器错误:在从初始化程序返回之前,未在所有路径上调用“self.init”【英文标题】:CoreData and Codable class compiler error: 'self.init' isn't called on all paths before returning from initializer 【发布时间】:2019-02-28 22:41:16 【问题描述】:

按照此答案的说明进行操作后:https://***.com/a/46917019/6047611

我遇到编译器错误'self.init' isn't called on all paths before returning from initializer. super.init() is not allowed。但是,调用self.init(entity: entity, insertInto: context) 然后以某种方式初始化类中的所有属性并不能完全初始化类。

我迷路了。我对 CoreData 还很陌生,还不太适应,所以我希望这是我自己无知的问题。有关如何解决此错误的任何想法?

import Foundation
import CoreData

class Product: NSManagedObject, Encodable, Decodable

    @NSManaged var mongoID:[String:String]
    @NSManaged var title:String
    @NSManaged var productID:Int
    @NSManaged var mpn:String
    @NSManaged var listPrice:Float
    @NSManaged var price:Float
    @NSManaged var uom:String
    @NSManaged var uomQty:Int
    @NSManaged var inventory:Float
    @NSManaged var minSaleQty:Int
    @NSManaged var desc:String

    @NSManaged var categories:[String]
    @NSManaged var imageURL:String
    @NSManaged var upc:String
    @NSManaged var quantity:Int
    @NSManaged var disc:Bool

    enum CodingKeys: String, CodingKey 
        case mongoID = "mongoID"
        case title = "title"
        case productID = "productID"
        case mpn = "mpn"
        case listPrice = "listPrice"
        case price = "price"
        case uom = "uom"
        case uomQty = "uomQty"
        case inventory = "inventory"
        case minSaleQty = "minSaleQty"
        case desc = "desc"
        case categories = "categories"
        case imageURL = "imageURL"
        case upc = "upc"
        case quantity = "quantity"
        case disc = "disc"
    

    required convenience init(from decoder:Decoder) throws
    
        guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else  print("failed context get"); return 
        guard let entity = NSEntityDescription.entity(forEntityName: "Product", in: context) else  print("failed entity init"); return 


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

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

        self.mongoID = try container.decodeIfPresent([String:String].self, forKey: .mongoID) ?? ["$id":"nil"]
        self.title = try container.decodeIfPresent(String.self, forKey: .title) ?? ""
        self.productID = try container.decodeIfPresent(Int.self, forKey: .productID) ?? 0
        self.mpn = try container.decodeIfPresent(String.self, forKey: .mpn) ?? ""
        self.listPrice = try container.decodeIfPresent(Float.self, forKey: .listPrice) ?? 0.0
        self.price = try container.decodeIfPresent(Float.self, forKey: .price) ?? 0.0
        self.uom = try container.decodeIfPresent(String.self, forKey: .uom) ?? ""
        self.uomQty = try container.decodeIfPresent(Int.self, forKey: .uomQty) ?? 0
        self.inventory = try container.decodeIfPresent(Float.self, forKey: .inventory) ?? 0.0
        self.minSaleQty = try container.decodeIfPresent(Int.self, forKey: .minSaleQty) ?? 0
        self.desc = try container.decodeIfPresent(String.self, forKey: .desc) ?? ""

        self.categories = try container.decodeIfPresent([String].self, forKey: .categories) ?? [""]
        self.imageURL = try container.decodeIfPresent(String.self, forKey: .imageURL) ?? ""
        self.upc = try container.decodeIfPresent(String.self, forKey: .upc) ?? ""
        self.quantity = try container.decodeIfPresent(Int.self, forKey: .quantity) ?? 0
        self.disc = try container.decodeIfPresent(Bool.self, forKey: .disc) ?? false
    //'self.init' isn't called on all paths before returning from initializer

    public func encode(to encoder: Encoder) throws
    

    



extension CodingUserInfoKey 
    static let context = CodingUserInfoKey(rawValue: "context")

【问题讨论】:

【参考方案1】:

此编译器错误与 Core Data 无关。这是由在调用self.init 之前可以return 的两个guard 语句引起的。

在下面的语句中,如果context 为nil,则else 条件将打印“失败的上下文获取”然后return

guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext 
else  print("failed context get"); return 

您正试图在调用 self.init 之前返回。这是不允许的。您的便利初始化程序必须返回正确初始化的对象。

但是,如果不能满足guard 语句中的任何一个,你有一个出路:你可以throw 一个例外。然后调用者有责任以任何有意义的方式处理异常。

为此,您需要创建一个符合Error 协议的enum,例如:

enum ProductError: Error 
    case contextMissing
    case entityCreationFailed

然后您可以像这样重写guard 语句:

guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext 
else  print("failed context get"); throw ProductError.contextMissing 

创建Product 时,您可以这样做:

let product = try? Product(from: decoder)  
//product is an optional, might be nil

或者这个:

if let product = try? Product(from: decoder) 
    //product is not an optional, cannot be nil

【讨论】:

以上是关于CoreData 和 Codable 类编译器错误:在从初始化程序返回之前,未在所有路径上调用“self.init”的主要内容,如果未能解决你的问题,请参考以下文章

Codable和CodingKeys

如何在 Core Data 中使用 swift 4 Codable?

如何在 Core Data 中使用 swift 4 Codable?

如何在 Core Data 中使用 swift 4 Codable?

CoreData:错误:无法在 NSManagedObject 类“BNRItem”上调用指定的初始化程序

如何在目标 c 数据模型类中使用 Codable 协议?