【问题标题】:Swift NSCoder NSKeyArchiver: unexpectedly found nil while unwrappingSwift NSCoder NSKeyArchiver:解包时意外发现 nil
【发布时间】:2017-11-13 09:20:05
【问题描述】:

使用 NSCoder 从 json 归档数组中的自定义对象时,错误会导致崩溃。

导致崩溃的错误:

致命错误:在展开可选值时意外发现 nil

错误代码:我在retrieveData() 中包含了完整代码

// NSCoding *Error causes Crash here*
let blogList: NSObject = ((jsonArray[i]) as! NSObject).value(forKey: "blogList") as! NSObject

MainController.swift

// Retrieving Data from Server *Clean Code*
func retrieveData() {

    let getDataURL = "http://blogtest.com/receiving.php"
    let url: NSURL = NSURL(string: getDataURL)!

    do {
        let data: Data = try Data(contentsOf: url as URL)
        jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray

        // Looping through jsonArray
        for i in 0..<jsonArray.count {

            // Create Blog Object
            let bID: String = (jsonArray[i] as AnyObject).object(forKey: "id") as! String
            let bName: String = (jsonArray[i] as AnyObject).object(forKey: "blogName") as! String
            let bStatus1: String = (jsonArray[i] as AnyObject).object(forKey: "blogStatus1") as! String
            let bStatus2: String = (jsonArray[i] as AnyObject).object(forKey: "blogStatus2") as! String
            let bURL: String = (jsonArray[i] as AnyObject).object(forKey: "blogURL") as! String
            let bType: String = (jsonArray[i] as AnyObject).object(forKey: "blogType") as! String
            let bDate: String = (jsonArray[i] as AnyObject).object(forKey: "blogDate") as! String
            let bPop: String = (jsonArray[i] as AnyObject).object(forKey: "blogPop") as! String

            // NSCoding *Error causes Crash here*
            let blogList: NSObject = ((jsonArray[i]) as! NSObject).value(forKey: "blogList") as! NSObject

            // Add Blog Objects to mainArray
            mainArray.append(Blog(blogName: bName, andBlogStatus1: bStatus1, andBlogStatus2: bStatus2, andBlogURL: bURL, andBlogID: bID, andBlogType: bType, andBlogDate: bDate, andBlogPop: bPop, blogList: blogList as! [Blog]))
        }
    }
    catch {
        print("Error: (Retrieving Data)")
    }
    myTableView.reloadData()
}

Blog.swift - 处理博客自定义对象

import UIKit

class Blog: NSObject, NSCoding {

// Strings
var blogName: String
var blogStatus1: String
var blogStatus2: String
var blogURL: String
var blogID: String
var blogType: String
var blogDate: String
var blogPop: String
var blogList : [Blog] // NSCoding

// Converting Strings into Objects
init(blogName bName: String,
     andBlogStatus1 bStatus1: String,
     andBlogStatus2 bStatus2: String,
     andBlogURL bURL: String,
     andBlogID bID: String,
     andBlogType bType: String,
     andBlogDate bDate: String,
     andBlogPop bPop: String,
     blogList : [Blog]) // To NSCoding
{

    self.blogName = bName
    self.blogStatus1 = bStatus1
    self.blogStatus2 = bStatus2
    self.blogURL = bURL
    self.blogID = bID
    self.blogType = bType
    self.blogDate = bDate
    self.blogPop = bPop
    self.blogList = blogList // NSCoding
    super.init()
}

// NSCoding
convenience required init?(coder aDecoder: NSCoder) {
    self.init (coder : aDecoder)
    self.blogName = aDecoder.decodeObject(forKey: "blogName") as! String
    self.blogStatus1 = aDecoder.decodeObject(forKey: "blogStatus1") as! String
    self.blogStatus2 = aDecoder.decodeObject(forKey: "blogStatus2") as! String
    self.blogURL = aDecoder.decodeObject(forKey: "blogURL") as! String
    self.blogID = aDecoder.decodeObject(forKey: "blogID") as! String
    self.blogType = aDecoder.decodeObject(forKey: "blogType") as! String
    self.blogDate = aDecoder.decodeObject(forKey: "blogDate") as! String
    self.blogPop = aDecoder.decodeObject(forKey: "blogPop") as! String
    self.blogList = aDecoder.decodeObject(forKey: "blogs") as! [Blog]
}

func encode(with aCoder: NSCoder) {
    aCoder.encode(blogName, forKey: "blogName")
    aCoder.encode(blogStatus1, forKey: "blogStatus1")
    aCoder.encode(blogStatus2, forKey: "blogStatus2")
    aCoder.encode(blogURL, forKey: "blogURL")
    aCoder.encode(blogID, forKey: "blogID")
    aCoder.encode(blogType, forKey: "blogType")
    aCoder.encode(blogDate, forKey: "blogDate")
    aCoder.encode(blogPop, forKey: "blogPop")
    aCoder.encode(blogList, forKey: "blogs")
 }
}

【问题讨论】:

    标签: ios swift nscoding nskeyedarchiver


    【解决方案1】:

    这次崩溃与 NSCoder 无关。签入调试器或记录 value(forKey: "blogList") 的值。它很可能不存在。你能给出你在网络响应中得到的样本响应吗?

    另外,我建议不要使用强制解包,而是使用带保护的可选,因为服务器响应不可信,这可能会在未来开始导致崩溃

    你应该把构造函数改成下面

    init?(id:String, info:[String:AnyObject]){
        guard let name = info["blogName"] as? String, let blogStatus1 = info["blogStatus1"] as? String  ... so on .. else { 
            return nil
        } 
    
        blogName = name
        .
        . initialize all field like these
    
        // for  blog list
        var tmpBlogList = [BlogList]()
        if let tmpBlogListInfo = info["blogList"] as? [[String:AnyObject]]
        {
            for info in tmpBlogListInfo
            {
                 let childId = info["blogID"] as? String
                 if let blog = Blog(id:childId, info) {
                      tmpBlogList.append(blog)
                 }
            }
        }
        blogList = tmpBlogList
    }
    
    
    //Replace mainArray.append(Blog(blogName: bName, andBlogStatus1: bStatus1, andBlogStatus2: bStatus2, andBlogURL: bURL, andBlogID: bID, andBlogType: bType, andBlogDate: bDate, andBlogPop: bPop, blogList: blogList as! [Blog]))
    if let blog = Blog(id: bID, info: blogList) {
          mainArray.append(blog)
    }
    

    【讨论】:

    • 所以崩溃是因为“blogList”是空的?我必须禁用 NSCoder 才能打印出网络存储对象的“mainArray”。我无法打印出“blogList”,因为它在该行中崩溃了。在不使用 NSCoder 的情况下打印 mainArray。 This is mainArray [&lt;BlogTest.Blog: 0x600000194770&gt;, &lt;BlogTest.Blog: 0x600000194840&gt;, &lt;BlogTest.Blog: 0x600000194910&gt;, &lt;BlogTest.Blog: 0x6000001949e0&gt;, &lt;BlogTest.Blog: 0x600000194ab0&gt;](因为不合适所以缩短了)
    • 如何防止崩溃?
    • 在阅读 blogList 之前可以打印 print("index: (i) , content: (String(describing: jsonArray[i]))")。
    • 您将 blogList 投射为! [博客],它也会导致崩溃。您正在从服务器下载数据并使用 JSONSerialization 解析数据,而不是从 json 读取 blogList。它使 blogList 成为 Blog 对象的数组。它很可能是 [[String:AnyObject]] 的格式
    • 好的,所以我更改了代码,清理了retrieveData(),添加了UserDefaults.standard 以保存archivedData,检查了密钥是否存在并且确实存在,并且它不再因错误而崩溃这篇文章说,但在self.init (coder : aDecoder) 行它的崩溃说“EXC_BAD_ACCESS”。我应该发一个新帖子还是替换这个?
    猜你喜欢
    • 2016-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-06
    相关资源
    最近更新 更多