简体   繁体   中英

Shows an error Non - fail able initializer “init(coder:)” error

When i'm trying to run the int(coder:) it shows this error, i don't know why ?? "Non failable initializer requirement init(coder:) cannot be satisfied by a failable initalizer ('init?')"

class Note: NSObject, NSCoding {

    var name: String
    var photo: UIImage?
    var rating: Int

    static let DocumentsDirectory: AnyObject = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
    static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("notes")

    struct PropertyKey {
    static let nameKey = "name"
    static let photoKey = "photo"
    static let ratingKey = "rating"
     }


    init?(name: String, photo: UIImage?, rating: Int) {

    self.name = name
    self.photo = photo
    self.rating = rating

    super.init()

    // Initialization should fail if there is no name or if the rating is negative.
    if name.isEmpty || rating < 0 {
        return nil
        }

    }

    func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(name, forKey: PropertyKey.nameKey)
    aCoder.encodeObject(photo, forKey: PropertyKey.photoKey)
    aCoder.encodeInteger(rating, forKey: PropertyKey.ratingKey)
    }

    required convenience init?(coder aDecoder: NSCoder) {

    let name = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as! String

    // Because photo is an optional property of Meal, use conditional cast.
    let photo = aDecoder.decodeObjectForKey(PropertyKey.photoKey) as? UIImage

    let rating = aDecoder.decodeIntegerForKey(PropertyKey.ratingKey)

    // Must call designated initializer.
    self.init(name: name, photo: photo, rating: rating)

    }


}

i'm using xcode 6 , and by the way when this code runs in xcode 7 it do not show any errors , what is the reason ?

So I found a way to make it work, the 'init(coder:)' method can't be failable so what I did was override the 'init()' method to be called by the 'init(coder:)' method because it needs to call 'self.init()'. So here is the code:

class Meal: NSObject, NSCoding {

// MARK: Archiving Paths
static let DocumentsDirectory   = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL           = DocumentsDirectory.URLByAppendingPathComponent("meals")

// MARK: Properties
var name:   String
var rating: Int
var photo:  UIImage?

// MARK: Types
struct PropertyKey {
    static let nameKey      = "name"
    static let photoKey     = "photo"
    static let ratingKey    = "rating"
}

// MARK: Initialization

init? (name: String, rating: Int, photo: UIImage?) {
    // Intialize stored properties.
    self.name   = name
    self.rating = rating
    self.photo  = photo

    super.init()

    // Initialization should fail if there is no name or if the rating is negative.
    if self.name.isEmpty || (rating < 0) {
        return nil
    }
}

override private init () {
    self.name   = ""
    self.rating = 0
    self.photo  = nil
}

// MARK: NSCoding

func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(self.name, forKey: PropertyKey.nameKey)
    aCoder.encodeObject(self.photo, forKey: PropertyKey.photoKey)
    aCoder.encodeObject(self.rating, forKey: PropertyKey.ratingKey)
}

convenience required init(coder aDecoder: NSCoder) {
    self.init()

    let name    = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as! String
    if let rating  = aDecoder.decodeIntegerForKey(PropertyKey.ratingKey) {
        self.rating = rating
    }

    // Because photo is an optional property of Meal, use conditional cast.
    let photo   = aDecoder.decodeObjectForKey(PropertyKey.photoKey) as? UIImage

    self.name   = name
    self.photo  = photo
}
}

I made the 'init()' method private so that only methods inside the class can call it. I also had to optional unwrap the rating because the app was crashing complaining about not being able to unarchive Int with the ratingKey.

Speculation on my part as to specifics, but, generally - the precise syntax and semantics of Swift changed significantly between Xcode 6 (say, Xcode 6.4) and the new Xcode 7 betas. Xcode 6.4 supports / uses Swift language version 1.2; Xcode 7 uses Swift language version 2.0 (beta)

Found a solution to run above code in xcode6 , should remove "convenience" from the init(coder aDecoder) and use super.init() instead of self.init(name: name, photo: photo, rating: rating) then it works perfectly

class Note: NSObject, NSCoding {

    var name: String
    var photo: UIImage?
    var rating: Int

    struct PropertyKey {
        static let nameKey = "name"
        static let photoKey = "photo"
        static let ratingKey = "rating"
    }

    init(name: String, photo: UIImage?, rating: Int) {

        self.name = name
        self.photo = photo
        self.rating = rating

        super.init()

        // Initialization should fail if there is no name or if the rating is negative.

    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(name, forKey: PropertyKey.nameKey)
        aCoder.encodeObject(photo, forKey: PropertyKey.photoKey)
        aCoder.encodeInteger(rating, forKey: PropertyKey.ratingKey)
    }



    required init(coder aDecoder: NSCoder) {
        self.name = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as! String

        // Because photo is an optional property of Meal, use conditional cast.
        self.photo = aDecoder.decodeObjectForKey(PropertyKey.photoKey) as? UIImage

        self.rating = aDecoder.decodeIntegerForKey(PropertyKey.ratingKey) as Int

        // Must call designated initializer.
        super.init()
    }



  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM