无法在 UserDefaults 中存储自定义对象数组

时间:2021-02-20 00:21:48

标签: ios swift

我有一个名为 Badge 的自定义对象,我有一个 Badges ([B​​adge]) 数组,我想将它们存储在 UserDefaults 中。我相信我可能做错了。我能够构建我的代码,但在 getBadges() 中启动时出现以下错误:Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value**. Can someone help。我已经尝试过 here 的解决方案,但没有成功。

//
//  Badge.swift
//

import Foundation

class Badge: NSObject {
    var name: String
    var info: String
    var score: Float?
    
    init(name: String, info: String, score: Float?) {
        self.name = name
        self.info = info
        self.score = score
    }
    
    static func ==(lhs: Badge, rhs: Badge) -> Bool {
        return lhs.name == rhs.name
    }
    
    func encodeWithCoder(coder: NSCoder) {
        coder.encode(self.name, forKey: "name")
        coder.encode(self.info, forKey: "info")
    }
}

//
//  BadgeFactory.swift
//

import Foundation

class BadgeFactory {
    let defaults: UserDefaults
    var badges: [Badge] = []
    var userBadges: [Badge] = []
    static let b = "Badges"
    
    init() {
        
        self.defaults = UserDefaults.standard
        self.userBadges = self.getBadges()

    }
    
    func addBadges(score: Float) -> [Badge]
    {
        var new_badges: [Badge] = []
        
        for badge in self.badges {
            if (!self.checkIfUserHasBadge(badge: badge) && badge.score != nil && score >= badge.score!) {
                new_badges.append(badge)
                self.userBadges.append(badge)
            }
        }
        
        self.defaults.set(self.userBadges, forKey: BadgeFactory.b)
        
        return new_badges
    }
    
    func checkIfUserHasBadge(badge: Badge) -> Bool
    {
        if self.badges.contains(badge) {
            return true
        }
        else {
            return false
        }
    }
    
    func getBadges() -> [Badge] {
        return self.defaults.array(forKey: BadgeFactory.b) as! [Badge]
    }
    
    func loadDefaultBadges() {
        // Score badges.
        self.badges.append(Badge(name: "Badge1", info: "My cool badge", score: 80))
        self.badges.append(Badge(name: "Badge2", info: "My second cool badge", score: 90))
    }
        
}
//
//  ViewController.swift
//

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        var bf = BadgeFactory()
        bf.getBadges()
        bf.addBadges(score: 85)
    }

}

2 个答案:

答案 0 :(得分:1)

此错误的原因位于您的 getBadges() 函数中:

func getBadges() -> [Badge] {
    return self.defaults.array(forKey: BadgeFactory.b) as! [Badge]
}  

使用 as! 您隐式地解包您期望的数组。但是只要您没有向这个 userDefaults 键写入数据,array(forKey:) 将始终返回 nil
因此,您需要在此处使用安全解包,例如:
return self.defaults.array(forKey: BadgeFactory.b) as? [Badge] ?? []


但这不是唯一的问题。就像您已经偶然发现的那样,您仍然需要实现您发布的线程的解决方案。自定义 NSObjects 不能在没有编码的情况下存储在默认值中。
您需要在 NSCoding protocol 类中实现 Badge(缺少 init(coder:))并使用 Unarchiver 进行读取,以及使用 Archiver 将数据写入默认值。

所以你的代码应该是这样的:

class Badge: NSObject, NSCoding {
    var name: String
    var info: String
    var score: Float?
    
    init(name: String, info: String, score: Float?) {
        self.name = name
        self.info = info
        self.score = score
    }

    required init?(coder: NSCoder) {
        self.name = coder.decodeObject(forKey: "name") as! String
        self.info = coder.decodeObject(forKey: "info") as! String
        self.score = coder.decodeObject(forKey: "score") as? Float
    }
    
    static func ==(lhs: Badge, rhs: Badge) -> Bool {
        return lhs.name == rhs.name
    }
    
    func encodeWithCoder(coder: NSCoder) {
        coder.encode(self.name, forKey: "name")
        coder.encode(self.info, forKey: "info")
        coder.encode(self.score, forKey: "score")
    }
}  
class BadgeFactory {
    ...

    func addBadges(score: Float) -> [Badge] {
        ...
        let data = NSKeyedArchiver.archivedData(withRootObject: self.userBadges)
        defaults.set(data, forKey: BadgeFactory.b)
        ...
    }

    func getBadges() -> [Badge] {
        guard let data = defaults.data(forKey: BadgeFactory.b) else { return [] }
        return NSKeyedUnarchiver.unarchiveObject(ofClass: Badge, from: data) ?? [] 
    }
    
    ...
}

答案 1 :(得分:0)

错误可能来自您 viewDidLoad 中的这一行:

bf.getBadges()

这将尝试执行 self.defaults.array(forKey: BadgeFactory.b) as! [Badge]

此时 UserDefaults 为空(因为您在调用 .addBadges 或为键提供任何其他值之前这样做了)。因此,self.defaults.array(forKey: BadgeFactory.b) 将评估为 nil,并且强制转换 as! [Badge] 将在运行时失败并显示您提供的消息。

为了解决我会像这样调整函数:

func getBadges() -> [Badge] {
    return (self.defaults.array(forKey: BadgeFactory.b) as? [Badge]) ?? []
}
相关问题