ios - Swift:无法正确访问 JSON 代码。 (打开包装时发现为零)

标签 ios json swift fatal-error nscoding

使用 Swift 3

当我单击 MainController.swift 中的按钮 followButtonClick 时,发生错误导致崩溃。 blogID 结果为零,但是当我打印 mainArray 时,所有对象都在那里,所以我的代码中有一些问题无法修复。 blogID 未正确访问。

fatal error: unexpectedly found nil while unwrapping an Optional value

我没有使用 NSCoder 和 UserDefaults 来保存 mainArrayfollowedArray 的整个数组,而是保存后面的 blogsID 然后显示我们通过抓取已保存 ID 的对象来保存这些对象。

MainController.swift

var mainArray = [Blog]()
var followedArray = [Blog]()
var filteredArray = [Blog]()
var followedIdentifiers = Set<String>()

// viewDidLoad
override func viewDidLoad() {
    super.viewDidLoad()

    retrieveDataFromServer()
    loadUserDefaults()
}

// Title for Header
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

if !(searchController.isActive && searchController.searchBar.text != "") {

    if section == 0 {
        return "Followed Blogs"
    }
    else {
        return "All Blogs"
    }
}
return "Filtered Blogs"
}

// Number of Rows in Section
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

if !(searchController.isActive && searchController.searchBar.text != "") {

    if section == 0 {

        return followedArray.count
    }
    else if (section == 1) {

        return mainArray.count
    }
}
return filteredArray.count
}

// CellForRowAt indexPath
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let CellIdentifier = "Cell"
var cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! CustomCell

if cell != cell {
    cell = CustomCell(style: UITableViewCellStyle.default, reuseIdentifier: CellIdentifier)
}

// Configuring the cell
var blogObject: Blog

if !(searchController.isActive && searchController.searchBar.text != "") {
    if indexPath.section == 0 {
        blogObject = followedArray[indexPath.row] 
        cell.populateCell(blogObject, isFollowed: true, indexPath: indexPath, parentView: self)
    }
    else if indexPath.section == 1 {
        blogObject = mainArray[indexPath.row] 
        cell.populateCell(blogObject, isFollowed: false, indexPath: indexPath, parentView: self)
    }
}
else {
    blogObject = filteredArray[indexPath.row] 
    cell.populateCell(blogObject, isFollowed: false, indexPath: indexPath, parentView: self)
}

return cell
}

// Follow Button
@IBAction func followButtonClick(_ sender: UIButton!) {

    // Adding row to tag
    let buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: self.myTableView)
    if let indexPath = self.myTableView.indexPathForRow(at: buttonPosition) {

        // Showing Status Labels
        let cell = self.myTableView.cellForRow(at: indexPath) as! CustomCell
        cell.firstStatusLabel.isHidden = false
        cell.secondStatusLabel.isHidden = false

        // Change Follow to Following
        (sender as UIButton).setImage(UIImage(named: "follow.png")!, for: .normal)
        cell.followButton.isHidden = true
        cell.followedButton.isHidden = false

        // Checking wether to import from mainArray or filteredArray to followedArray
        if !(searchController.isActive && searchController.searchBar.text != "") {

            self.myTableView.beginUpdates()

            // -*- Error breaks here -*- 
            // Save identifier into followedIdentifier array
     self.followedIdentifiers.insert(mainArray[indexPath.row].blogID)

            // ----- Inserting Cell to followedArray -----
            followedArray.insert(mainArray[indexPath.row], at: 0)
            myTableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .fade)

            // ----- Removing Cell from mainArray -----
            mainArray.remove(at: indexPath.row)
            let rowToRemove = indexPath.row
            self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 1)], with: .fade)

            self.myTableView.endUpdates()

            myTableView.reloadData()

            // After Updating Table, Save the Archived to UserDefaults
            saveUserDefaults()
        }
        else {

            self.myTableView.beginUpdates()

            // Remove identifier into followedIdentifier array
 self.followedIdentifiers.remove(followedArray[indexPath.row].blogID)

            // ----- Inserting Cell to followedArray -----
            let blogObject: Blog = filteredArray[indexPath.row]
            let indexOfObjectInArray = mainArray.index(of: blogObject)

            followedArray.insert(blogObject, at: 0)

            // ----- Removing Cell from filteredArray -----
            filteredArray.remove(at: indexPath.row)
            mainArray.remove(at: indexOfObjectInArray!)
            let rowToRemove = indexPath.row
            self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 0)], with: .fade)

            self.myTableView.endUpdates()

            myTableView.reloadData()

            // After Updating Table, Save the Archived to UserDefaults
            saveUserDefaults()
        }
    }
}

// Unfollow Button
@IBAction func followedButtonClick(_ sender: UIButton!) {

    // Adding row to tag
    let buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: self.myTableView)
    if let indexPath = self.myTableView.indexPathForRow(at: buttonPosition) {

        // Hiding Status Labels
        let cell = self.myTableView.cellForRow(at: indexPath) as! CustomCell
        cell.firstStatusLabel.isHidden = true
        cell.secondStatusLabel.isHidden = true

        // Change Following to Follow
        (sender as UIButton).setImage(UIImage(named: "followed.png")!, for: .normal)
        cell.followButton.isHidden = false
        cell.followedButton.isHidden = true

        self.myTableView.beginUpdates()

        // Remove identifier into followedIdentifier array
        self.followedIdentifiers.remove(followedArray[indexPath.row].blogID)

        // ----- Inserting Cell to mainArray -----
        mainArray.insert(followedArray[indexPath.row], at: 0)
        myTableView.insertRows(at: [IndexPath(row: 0, section: 1)], with: .fade)

        // ----- Removing Cell from followedArray -----
        followedArray.remove(at: indexPath.row)
        let rowToRemove = indexPath.row
        self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 0)], with: .fade)

        self.myTableView.endUpdates()

        myTableView.reloadData()

        // After Updating Table, Save the Archived to UserDefaults
        saveUserDefaults()
    }
}

// Saving UserDefaults
func saveUserDefaults() {

    let key = "followedID"
    UserDefaults.standard.setValue(self.followedIdentifiers, forKey: key)
    UserDefaults.standard.synchronize()
}

// Load UserDefaults
func loadUserDefaults() {

    let key = "followedID"
    UserDefaults.standard.setValue(Array(self.followedIdentifiers), forKey: key)
    self.followedIdentifiers = Set(UserDefaults.standard.stringArray(forKey: key)!)
}

// Retrieving Data from Server
func retrieveDataFromServer() {

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

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

        // Clear the arrays
        self.followedArray = [Blog]()
        self.mainArray = [Blog()]

        // Looping through jsonArray
        for jsonObject in jsonArray {

            if let blog = Blog.createBlog(from: jsonObject as AnyObject) {

                // Check if identifiers match
                if followedIdentifiers.contains(blog.blogID) {
                    self.followedArray.append(blog)
                } else {
                    self.mainArray.append(blog)
                }
            }
        }
    } catch {
        print("Error: (Retrieving Data)")
    }
    myTableView.reloadData()
}

Blog.swift - 处理博客对象

import UIKit

class Blog: NSObject, NSCoding {

var blogName: String!
var blogStatus1: String!
var blogStatus2: String!
var blogURL: String!
var blogID: String!
var blogType: String!
var blogDate: String!
var blogPop: String!

static func createBlog(from jsonObject: AnyObject) -> Blog? {

    guard let bID: String = jsonObject.object(forKey: "id") as? String,
        let bName: String = jsonObject.object(forKey: "blogName") as? String,
        let bStatus1: String = jsonObject.object(forKey: "blogStatus1") as? String,
        let bStatus2: String = jsonObject.object(forKey: "blogStatus2") as? String,
        let bURL: String = jsonObject.object(forKey: "blogURL") as? String,
        let bType: String = jsonObject.object(forKey: "blogType") as? String,
        let bDate: String = jsonObject.object(forKey: "blogDate") as? String,
        let bPop: String = jsonObject.object(forKey: "blogPop") as? String

        else {
          print("Error: (Creating Blog Object)")
          return nil
}

let blog = Blog()
    blog.blogName = bName
    blog.blogStatus1 = bStatus1
    blog.blogStatus2 = bStatus2
    blog.blogURL = bURL
    blog.blogID = bID
    blog.blogType = bType
    blog.blogDate = bDate
    blog.blogPop = bPop
    return blog
}

convenience required init?(coder aDecoder: NSCoder) {
    self.init ()
    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
}

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")
 }
}

最佳答案

您面临的直接问题是您有一个隐式解包的可选 blogID尚未分配值。造成这种情况的根本原因是您使用隐式展开的选项掩盖了它,因为它没有透露您无法初始化 Blog 的位置。实例正确。

而不是使用静态函数来创建 Blog从 JSON 字典来看,您应该使用可失败的初始化程序。这将允许您在适当的情况下使用非可选属性,并在对象未正确初始化时给您一个编译器或运行时错误。

import Foundation

class Blog: NSObject, NSCoding {

    var blogName: String
    var blogStatus1: String
    var blogStatus2: String
    var blogURL: String     // Note this should probably be a URL rather than a String
    var blogID: String
    var blogType: String
    var blogDate: String    // Note this should probably be a Date rather than a String
    var blogPop: String


    private init (name: String,status1: String,status2: String,url: String,id: String,type: String,date: String,pop: String) {
        blogName = name
        blogStatus1 = status1
        blogStatus2 = status2
        blogURL = url
        blogID = id
        blogType = type
        blogDate = date
        blogPop = pop
        super.init()
    }

    convenience init?(jsonObject: [String:Any]) {

        guard let bID = jsonObject["id"] as? String,
            let bName = jsonObject["blogName"] as? String,
            let bStatus1 = jsonObject["blogStatus1"] as? String,
            let bStatus2 = jsonObject["blogStatus2"] as? String,
            let bURL = jsonObject["blogURL"] as? String,
            let bType = jsonObject["blogType"] as? String,
            let bDate = jsonObject["blogDate"] as? String,
            let bPop = jsonObject["blogPop"] as? String

            else {
                print("Error: (Creating Blog Object)")
                return nil
        }

        self.init(name: bName, status1: bStatus1, status2: bStatus2, url: bURL, id: bID, type: bType, date: bDate, pop: bPop)

    }

    convenience required init?(coder aDecoder: NSCoder) {
        guard let blogName = aDecoder.decodeObject(forKey: "blogName") as? String,
            let blogStatus1 = aDecoder.decodeObject(forKey: "blogStatus1") as? String,
            let blogStatus2 = aDecoder.decodeObject(forKey: "blogStatus2") as? String,
            let blogURL = aDecoder.decodeObject(forKey: "blogURL") as? String,
            let blogID = aDecoder.decodeObject(forKey: "blogID") as? String,
            let blogType = aDecoder.decodeObject(forKey: "blogType") as? String,
            let blogDate = aDecoder.decodeObject(forKey: "blogDate") as? String,
            let blogPop = aDecoder.decodeObject(forKey: "blogPop") as? String else {
                print("Error: (Creating Blog Object)")
                return nil
        }
        self.init(name: blogName, status1: blogStatus1, status2: blogStatus2, url: blogURL, id: blogID, type: blogType, date: blogDate, pop: blogPop)
    }

    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")
    }
}

请注意,这可能不会立即解决显示 ID 的问题,但它将帮助您找到未初始化 Blog 的位置。正确实例

关于ios - Swift:无法正确访问 JSON 代码。 (打开包装时发现为零),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44709952/

相关文章:

javascript - 在 iPhone 上检测 “Done” 以获取 YouTube/Vimeo 视频(退出全屏)

ios - 如果我将我的应用程序移到后台然后在 iOS 中进入前台,则呈现的 View Controller 会被解散

c++ - 在运行时组装自己的对象

ios - 如何在swift中制作虚线?

ios - 在 iPad 应用程序中实现双重身份验证

ios - UIBezierPath 路径的交集

sql-server - 从关系数据库中提取 JSON 的通用方法?

php - 如何使用这样的脚本或文本编辑器批量更新位于 .html 文件中的基本一对一 key 结构?

ios - swift 将表格 View 的大小调整到最后一行

c - Swift 与 C 结构指针