【问题标题】:App Delegate weird error when trying to add element to NSUserDefaults尝试将元素添加到 NSUserDefaults 时,App Delegate 出现奇怪的错误
【发布时间】:2015-09-26 15:41:17
【问题描述】:

我在 Xcode 7 (Swift 2) 上运行我的应用程序时遇到了一个非常奇怪的错误,在我的应用程序的 App Delegate 类中显示“线程 1:信号 SIGABRT”运行错误消息。但是,我实际上已经在 App Delegate 类中多次收到此“线程 1:信号 SIGABRT”运行错误消息,主要是在我的代码中删除插座引用并忘记将其从情节提要中删除时。但这肯定是我第一次在尝试发出命令时遇到同样的错误:

let wasteGain = WastesGainsClass(value: enteredMoney, originOrCat: segControlArray[segControl.selectedSegmentIndex], specification: plusEspecTField.text!, date: dateArray, mode: "gain")

gains.append(wasteGain)

NSUserDefaults.standardUserDefaults().setObject(gains, forKey: "gains")

如果我只是评论 NSUserDefaults.standardUserDefaults().setObject(gains, forKey: "gains") 行,应用程序不会崩溃!所以错误可能就在那一行。

如果有人可以帮助我,我会非常感谢你。

PS:WastesGainsClass 格式是这样的:

class WastesGainsClass {

    var value:Int = 0
    var origin:String
    var specification:String
    var date:[String]
    var mode:String
    var rowMode:Int = 0

    init(value:Int, originOrCat:String, specification:String, date:[String], mode:String) {

        self.value = value
        self.origin = originOrCat
        self.specification = specification
        self.date = date
        self.mode = mode

    }

}

【问题讨论】:

  • 只有符合属性列表的类型(NSNumberNSStringNSDateNSData)和集合类型NSArrayNSDictionary可以用NSUserDefaults保存。集合类型还必须包含符合属性列表的类型。对于自定义类,您的类必须符合 NSCoding 才能使用 NSKeyedArchiver/Unarchiver 归档
  • @vadian 您应该将其添加为答案;这正是问题/解决方案所在。

标签: swift nsuserdefaults swift2 sigabrt


【解决方案1】:

来自documentation

NSUserDefaults 类提供了方便的访问方法 常见类型,例如浮点数、双精度数、整数、布尔值和 URL。一种 默认对象必须是一个属性列表,即(或 对于集合实例的组合):NSData,NSString, NSNumber、NSDate、NSArray 或 NSDictionary。如果你想存储任何 其他类型的对象,您通常应该将其归档以创建一个 NSData 的实例。

在 Swift 中你也可以使用:

  • IntUIntDoubleFloatBool 类型,因为它们会自动桥接到 NSNumber
  • String 桥接到NSString
  • [AnyObject] 因为它桥接到NSArray
  • [NSObject: AnyObject] 因为它桥接到NSDictionary

当然数组元素和字典值的类型必须是上述类型之一。字典键类型必须是NSString(或桥接String)。

要存储任何其他类的实例,您有两个选择:

  1. 您的自定义类必须是NSObject 的子类并符合 NSCoding 协议,然后您可以使用 NSKeyedArchiver.archivedDataWithRootObject() 将此类的对象归档到 NSData 并将其保存到 NSUserDefaults,然后从 NSUserDefaults 检索它并使用 NSKeyedUnarchiver.unarchiveObjectWithData() 取消归档:

    import Foundation
    
    class WastesGainsClass: NSObject, NSCoding {
        var value: Int
    
        init(value: Int) {
            self.value = value
        }
    
        required init(coder aDecoder: NSCoder) {
            value = aDecoder.decodeObjectForKey("value") as! Int
        }
    
        func encodeWithCoder(aCoder: NSCoder) {
            aCoder.encodeObject(value, forKey: "value")
        }
    }
    
    var gains = [WastesGainsClass(value: 1), WastesGainsClass(value: 2)]
    NSUserDefaults.standardUserDefaults().setObject(gains.map {  NSKeyedArchiver.archivedDataWithRootObject($0) }, forKey: "gains")
    
    if let gainsData = NSUserDefaults.standardUserDefaults().objectForKey("gains") as? [NSData] {
        gains = gainsData.map { NSKeyedUnarchiver.unarchiveObjectWithData($0) as! WastesGainsClass }
    }
    
  2. 您可以将自定义对象属性保存到字典并存储 NSUserDefaults中的字典:

    import Foundation
    
    class WastesGainsClass {
        var value: Int
    
        init(value: Int) {
            self.value = value
        }
    }
    
    extension WastesGainsClass {
        convenience init(dict: [NSObject: AnyObject]) {
            self.init(value: dict["value"] as? Int ?? 0)
        }
    
        func toDict() -> [NSObject: AnyObject] {
            var d = [NSObject: AnyObject]()
            d["value"] = value
            return d
        }
    }
    
    var gains = [WastesGainsClass(value: 1), WastesGainsClass(value: 2)]
    
    NSUserDefaults.standardUserDefaults().setObject(gains.map { $0.toDict() }, forKey: "gains")
    
    if let dicts = NSUserDefaults.standardUserDefaults().objectForKey("gains") as? [[NSObject: AnyObject]] {
        gains = dicts.map { WastesGainsClass(dict: $0) }
    }
    

【讨论】:

    【解决方案2】:

    NSUserDefaults 遗憾的是不能接受任意对象,只能接受可以在属性列表中编码的对象。请参阅Apple's reference guide for Property Lists 了解可以存储哪些对象。

    如果您需要保存多个WastesGainsClass 对象,您可能希望编写一个返回Dictionary 编码其属性列表可表示属性的方法,以及一个接受此类Dictionary 的初始化程序来恢复对象。

    但是,如果您确实需要像这样保存多个自定义对象,您可能根本不想使用NSUserDefaults。考虑一个基于文档的应用程序,并查看NSCoding

    【讨论】:

      【解决方案3】:

      您发布的代码尝试将一组自定义对象保存到 NSUserDefaults。你不能那样做。实现 NSCoding 方法没有帮助。 NSUserDefaults 中只能存储 NSArray、NSDictionary、NSString、NSData、NSNumber 和 NSDate 等内容。

      您需要将对象转换为 NSData(就像您在某些代码中所做的那样)并将该 NSData 存储在 NSUserDefaults 中。如果需要,您甚至可以存储 NSData 的 NSArray。

      看到这个帖子:Attempt to set a non-property-list object as an NSUserDefaults

      【讨论】:

      • NSCoding 协议在您使用NSKeyedArchiver.archivedDataWithRootObject() 将自定义类的实例归档到NSData 并稍后使用NSKeyedUnarchiver.unarchiveObjectWithData 检索它时使用。
      • @mixel:请再读一遍(并删除您的反对票):答案是相关的,该帖子实际上是所引用帖子的副本。好的,我也提到了 NSCodings,因为它在另一个答案(而不是这里)中提到了。但 Nicolas 遇到了完全相同的问题和错误
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多