NSKeyedArchiver.archivedData在Swift 3 iOS中无法正常工作

5

当我在iOS Swift中尝试编码我的自定义对象时,Xcode 8.3给出了以下错误:

未识别的选择器发送到实例0x60800166fe80 *** - [NSKeyedArchiver dealloc]:警告:在未调用-finishEncoding的情况下取消分配NSKeyedArchiver。

我的代码如下:

import UIKit import Foundation

class Place: NSObject {

    func setCustomObject(CustomObject obj:Any,Key key:String) {

        let encodedObject : Data = NSKeyedArchiver.archivedData(withRootObject: obj)
        UserDefaults.standard.set(encodedObject, forKey: key)

    }
}

你的 CustomObject 是否符合 NSCoding 协议? - Juri Noga
不需要 @njuri,是吗? - reza_khalafi
1
是的,我会在回答中进行描述。 - Juri Noga
2个回答

4

以下是一个如何使对象符合NSCoding的示例。基本上,您需要提供两种方法的实现 - required convenience init?(coder decoder: NSCoder)encode(with aCoder: NSCoder)

class Book: NSObject, NSCoding {
  var title: String?
  var pageCount: Int?

  // Memberwise initializer
  init(title: String,pageCount: Int) {
   self.title = title
   self.pageCount = pageCount
  }

  // MARK: NSCoding


  // Here you will try to initialize an object from archve using keys you did set in `encode` method.
  required convenience init?(coder decoder: NSCoder) {
    guard let title = decoder.decodeObject(forKey: "title") as? String else { return nil }

    self.init(title: title, pageCount: decoder.decodeInteger(forKey: "pageCount"))
  }

  // Here you need to set properties to specific keys in archive
  func encode(with aCoder: NSCoder) {
    aCoder.encode(self.title, forKey: "title")
    aCoder.encodeCInt(Int32(self.pageCount), forKey: "pageCount")
  }
}

我建议您将setCustomObject方法更改为以下内容:
此外,我建议您将setCustomObject方法更改为以下内容:
func setCustomObject(obj:NSCoding, key:String) {
  let encodedObject : Data = NSKeyedArchiver.archivedData(withRootObject: obj)
  UserDefaults.standard.set(encodedObject, forKey: key)
}

编译器这样做是为了防止您向NSKeyedArchiver传递一个不符合NSCoding协议的对象。

如果您不想在init方法中提供所有属性,可以使用默认值:

init(title : String? = nil, pageCount: Int? = nil){
  self.title = title
  self.pageCount = pageCount
}

现在您可以只使用Book()来初始化对象,而无需任何属性。

我在这个例子中遇到了一个主要的问题,我的对象有太多的属性,现在每次我想实例化新对象时都必须初始化所有不必要的属性 :| @njuri - reza_khalafi
我希望我的新对象实例包含空属性 @njuri - reza_khalafi
1
如果您的属性还不是可选的,请将其设置为可选,并将默认值 nil 改为 init 方法。请参阅更新后的答案。 - Juri Noga
谢谢,但现在当我尝试从userDefault检索我的对象时,我遇到了这个错误:fatal error: unexpectedly found nil while unwrapping an Optional value @njuri - reza_khalafi
1
你说你想让属性为 nil - 这就是错误提示的意思。 - Juri Noga
显示剩余2条评论

3
这里是解决方案,您需要实现两种方法。
编码方法:
func encode(with aCoder: NSCoder)

解码方法
 required init?(coder aDecoder: NSCoder)

完整的示例代码

class User: NSObject , NSCoding
{


var userID : Int = 0
var name : String = ""
var firstName : String = ""
var lastName : String = ""
var username : String = ""
var email : String = ""

override init(){
    super.init();
}

func encode(with aCoder: NSCoder) {

    aCoder.encode(self.userID,  forKey: "id");
    aCoder.encode(self.firstName,    forKey: "first_name");
    aCoder.encode(self.lastName,    forKey: "last_name");
    aCoder.encode(self.name,    forKey: "name");
    aCoder.encode(self.username,forKey: "username");
    aCoder.encode(self.email,   forKey: "email");
}

required init?(coder aDecoder: NSCoder) {
    super.init()

    self.userID     = aDecoder.decodeInteger(forKey: "id");
    self.firstName  = aDecoder.decodeObject(forKey: "first_name") as! String;
    self.lastName   = aDecoder.decodeObject(forKey: "last_name") as! String;
    self.name       = aDecoder.decodeObject(forKey: "name") as! String
    self.username   = aDecoder.decodeObject(forKey: "username") as! String
    self.email      = aDecoder.decodeObject(forKey: "email") as! String;
}

init(data : [String: AnyObject]) {

    super.init()

    self.userID = String.numberValue(data["user_id"]).intValue;
    self.firstName = String.stringValue(data["first_name"]);
    self.lastName = String.stringValue(data["last_name"]);
    self.email = String.stringValue(data["email"]);
    self.username = String.stringValue(data["user_name"]);
}

class func loadLoggedInUser()  -> User {

    if let  archivedObject = UserDefaults.standard.object(forKey:"CurrentUserAcc"){

        if let user  = NSKeyedUnarchiver.unarchiveObject(with: (archivedObject as! NSData) as Data) as? User {

            return user;
        }

    }

    return User()
}

func saveUser(){

    let archivedObject : NSData = NSKeyedArchiver.archivedData(withRootObject: self) as NSData

    UserDefaults.standard.set(archivedObject, forKey: "CurrentUserAcc");

    UserDefaults.standard.synchronize();
}

func deleteUser(){

    UserDefaults.standard.set(nil, forKey: "CurrentUserAcc")

    UserDefaults.standard.synchronize();
} 
}

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