NSUserDefaults 自定义对象 - 属性列表对格式无效:200(属性列表不能包含“CFType”类型的对象)

Posted

技术标签:

【中文标题】NSUserDefaults 自定义对象 - 属性列表对格式无效:200(属性列表不能包含“CFType”类型的对象)【英文标题】:NSUserDefaults Custom object - Property list invalid for format: 200 (property lists cannot contain objects of type 'CFType') 【发布时间】:2014-10-03 07:34:57 【问题描述】:

我一定是错误地使用了 NSUserDefaults 的自定义对象。错误“属性列表对格式无效:200(属性列表不能包含 'CFType' 类型的对象)”。下面是我的代码,Goal 类特别有趣,因为这是我采用 NSCoding 协议的地方。

此代码是全局的。

func saveGoals (goals : [Goal]) 
    var updatedGoals = NSKeyedArchiver.archivedDataWithRootObject(goals)
    NSUserDefaults.standardUserDefaults().setObject(updatedGoals, forKey: "Goals")
    NSUserDefaults.standardUserDefaults().synchronize()


func loadCustomObjectWithKey() -> [Goal] 
    if let encodedObject : NSData = NSUserDefaults.standardUserDefaults().objectForKey("Goals") as? NSData 
        var encodedObject : NSData? = NSUserDefaults.standardUserDefaults().objectForKey("Goals") as? NSData
        var goal : [Goal] = NSKeyedUnarchiver.unarchiveObjectWithData(encodedObject!) as [Goal]
        return goal
     else 
        return [Goal]()
    

这段代码在GoalsViewController中。

class GoalsViewController: MainPageContentViewController, UITableViewDelegate, UITableViewDataSource 
    @IBOutlet var tableView: GoalsTableView!

    var cell = GoalTableViewCell()

    var goalsArray : Array<Goal> = [] //

    override func viewDidLoad() 
        super.viewDidLoad()
        self.tableView.delegate = self
        self.tableView.dataSource = self

        if var storedGoals: [Goal] = loadCustomObjectWithKey() as [Goal]? 
            goalsArray = storedGoals
        
        //retrieve data.

        var goal = Goal(title: "Walk the Dog")
        goalsArray.append(goal)
        saveGoals(goalsArray)

        self.tableView?.reloadData()

        tableView.estimatedRowHeight = 44.0
        tableView.rowHeight = UITableViewAutomaticDimension

        var notification = NSNotificationCenter.defaultCenter()

        notification.addObserver(self, selector: "finishCreatingGoal:", name: "FinishCreatingGoal", object: nil)
    

func finishCreatingGoal(notification : NSNotification) 
        if (notification.name == "FinishCreatingGoal") 
            var userInfo = notification.userInfo!
            var text = userInfo["text"]! as String
            var index = userInfo["index"]! as Int

            var cell = self.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0)) as GoalTableViewCell
            goalsArray[index].title = cell.goalTextField.text
            saveGoalList(goalsArray)

            self.tableView.reloadData()
            self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0), atScrollPosition: UITableViewScrollPosition.Middle, animated: true)
        
    

此代码在目标类中。

import UIKit

class Goal : NSObject, NSCoding 

    var title : String? = ""
    var checkmarked : Bool? = false 
    var isLastCell : Bool? = false 
    var enabled : Bool? = true 

    var priority = Priority.defaultPriority

    override init() 
    

    init(title : String) 
        self.title = title
    

    func encodeWithCoder(aCoder: NSCoder) 
        aCoder.encodeObject(title!, forKey: "title")
        aCoder.encodeBool(checkmarked!, forKey: "checkmarked")
        aCoder.encodeBool(isLastCell!, forKey: "isLastCell")
        aCoder.encodeBool(enabled!, forKey: "enabled")
    

    required init(coder aDecoder: NSCoder) 
        title = aDecoder.decodeObjectForKey("title") as String!
        checkmarked = aDecoder.decodeBoolForKey("checkmarked") as Bool
        isLastCell = aDecoder.decodeBoolForKey("isLastCell") as Bool
        enabled = aDecoder.decodeBoolForKey("enabled") as Bool
    


【问题讨论】:

我猜你的目标对象的title 之一是零。这行:goalsArray[index].title = cell.goalTextField.text 是否保证将其设置为非零值? 是的,保证非零 【参考方案1】:

我将只从我拥有的工作项目中复制代码:

这是一个包含数学闪存卡游戏数据的 Game 对象类:

import Foundation

class GameData: NSObject 

    var sign: String = "+"
    var level: Int = 1
    var problems: Int = 10
    var time: Int = 30
    var skipWrong: Bool = true
    var usedTime: Int = 0
    var correctCount: Int = 0
    var correctTopNumber: [Int] = [Int]()
    var correctBottomNumber: [Int] = [Int]()
    var wrongTopNumber: [Int] = [Int]()
    var wrongBottomNumber: [Int] = [Int]()
    var date: NSDate = NSDate()

    func encodeWithCoder(aCoder: NSCoder!) 
        aCoder.encodeObject(sign, forKey: "sign")
        aCoder.encodeInteger(level, forKey: "level")
        aCoder.encodeInteger(problems, forKey: "problems")
        aCoder.encodeInteger(time, forKey: "time")
        aCoder.encodeBool(skipWrong, forKey: "skipWrong")
        aCoder.encodeInteger(usedTime, forKey: "usedTime")
        aCoder.encodeInteger(correctCount, forKey: "correctCount")
        aCoder.encodeObject(correctTopNumber, forKey: "correctTopNumber")
        aCoder.encodeObject(correctBottomNumber, forKey: "correctBottomNumber")
        aCoder.encodeObject(wrongTopNumber, forKey: "wrongTopNumber")
        aCoder.encodeObject(wrongBottomNumber, forKey: "wrongBottomNumber")
        aCoder.encodeObject(date, forKey: "date")
    

    init(coder aDecoder: NSCoder!) 
        sign = aDecoder.decodeObjectForKey("sign") as String
        level = aDecoder.decodeIntegerForKey("level")
        problems = aDecoder.decodeIntegerForKey("problems")
        time = aDecoder.decodeIntegerForKey("time")
        skipWrong = aDecoder.decodeBoolForKey("skipWrong")
        usedTime = aDecoder.decodeIntegerForKey("usedTime")
        correctCount = aDecoder.decodeIntegerForKey("correctCount")
        correctTopNumber = aDecoder.decodeObjectForKey("correctTopNumber") as Array
        correctBottomNumber = aDecoder.decodeObjectForKey("correctBottomNumber") as Array
        wrongTopNumber = aDecoder.decodeObjectForKey("wrongTopNumber") as Array
        wrongBottomNumber = aDecoder.decodeObjectForKey("wrongBottomNumber") as Array
        date = aDecoder.decodeObjectForKey("date") as NSDate
    

    override init() 
    

这部分看起来和你的差不多,但变量类型更多。归档器和检索器类与您不同:

import Foundation

class ArchiveGameData:NSObject 

    var documentDirectories:NSArray = []
    var documentDirectory:String = ""
    var path:String = ""

    func ArchiveResults(#dataSet: [GameData]) 
        documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
        documentDirectory = documentDirectories.objectAtIndex(0) as String
        path = documentDirectory.stringByAppendingPathComponent("results3.archive")

        if NSKeyedArchiver.archiveRootObject(dataSet, toFile: path) 
            //println("Success writing to file!")
         else 
            println("Unable to write to file!")
        
    

    func RetrieveGameData() -> NSObject 
        var dataToRetrieve = [GameData]()
        documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
        documentDirectory = documentDirectories.objectAtIndex(0) as String
        path = documentDirectory.stringByAppendingPathComponent("results3.archive")
        if let dataToRetrieve2 = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? [GameData] 
          dataToRetrieve = dataToRetrieve2
        
        return(dataToRetrieve)
    

最后,用于在 ViewController 中存储和检索的代码:

//retrieveing
    var gameDataArray = ArchiveGameData().RetrieveGameData() as [GameData]

//Archiving
      gameData = GameData() //create local object then append all the new data, then store it
gameData.sign = buttonStates.sign
        gameData.level = buttonStates.level
        gameData.problems = buttonStates.problems
        gameData.time = buttonStates.time
//etc. for all properties

      ArchiveGameData().ArchiveResults(dataSet: gameDataArray)

【讨论】:

Steve,我认为将 NSCoding 协议添加到您的类声明 GameData 中是个好主意

以上是关于NSUserDefaults 自定义对象 - 属性列表对格式无效:200(属性列表不能包含“CFType”类型的对象)的主要内容,如果未能解决你的问题,请参考以下文章

为啥 `NSUserDefaults` 不允许存储自定义类的对象?

NSUserDefaults读取和写入自定义对象

NSUserDefaults存储自定义对象

从 NSUserDefaults 检索自定义对象

将自定义对象存储到 NSMutableArray,然后将此 NSMutableArray 存储到 NSUserDefaults

如何在 Swift 中归档和取消归档自定义对象?或者如何在 Swift 中将自定义对象保存到 NSUserDefaults?