NSKeyedUnarchiver 导致崩溃
NSKeyUnarchiver causing Crash
Swift3,
我在我的 iOS 应用程序中使用 NSUserDefaults 来保存和加载 indexPath 以及发生操作的行的哪个部分。因为我的 [=42= 中的每一行都有一个按钮]看法。在 ViewDidLoad.
中重新加载 table 时再次加载
在 ViewDidLoad 中,我正在调用 fetch 函数,该函数应该保存和加载任何内容。
func fetchData() {
// request from remote or local
data = [testArray]
// Update the items to first section has 0 elements,
// and place all data in section 1
items = [[], data ?? []]
// apply ordering
applySorting() { "\([=10=])" }
// save ordering
saveSorting() { "\([=10=])" }
// refresh the table view
myTableView.reloadData()
}
在我的 buttonAction 中,我使用了 saveSorting() 函数。
func saveSorting(_ dataIdBlock: (Any) -> String) {
guard let items = self.items else { return }
for (section, rows) in items.enumerated() {
for (row, item) in rows.enumerated() {
let indexPath = IndexPath(row: row, section: section)
let dataId = dataIdBlock(item)
let ordering = DataHandling(dataId: dataId, indexPath: indexPath)
ordering.save(defaults: indexPath.defaultsKey)
}
}
}
这是我的断点图片,显示了日志以及它在代码中中断的位置。
非常感谢您帮助修复此崩溃。该应用程序甚至没有加载它在应用程序完全加载之前停留在白屏中。谢谢。
这是代码
class DataHandling: NSObject, NSCoding {
var indexPath: IndexPath?
var dataId: String?
init(dataId: String, indexPath: IndexPath) {
super.init()
self.dataId = dataId
self.indexPath = indexPath
}
required init(coder aDecoder: NSCoder) {
if let dataId = aDecoder.decodeObject(forKey: "dataId") as? String {
self.dataId = dataId
}
if let indexPath = aDecoder.decodeObject(forKey: "indexPath") as? IndexPath {
self.indexPath = indexPath
}
}
func encode(with aCoder: NSCoder) {
aCoder.encode(dataId, forKey: "dataId")
aCoder.encode(indexPath, forKey: "indexPath")
}
func save(defaults box: String) -> Bool {
let defaults = UserDefaults.standard
let savedData = NSKeyedArchiver.archivedData(withRootObject: self)
defaults.set(savedData, forKey: box)
return defaults.synchronize()
}
convenience init?(defaults box: String) {
let defaults = UserDefaults.standard
if let data = defaults.object(forKey: box) as? Data,
let obj = NSKeyedUnarchiver.unarchiveObject(with: data) as? DataHandling,
let dataId = obj.dataId,
let indexPath = obj.indexPath {
self.init(dataId: dataId, indexPath: indexPath)
} else {
return nil
}
}
class func allSavedOrdering(_ maxRows: Int) -> [Int: [DataHandling]] {
var result: [Int: [DataHandling]] = [:]
for section in 0...1 {
var rows: [DataHandling] = []
for row in 0..<maxRows {
let indexPath = IndexPath(row: row, section: section)
if let ordering = DataHandling(defaults: indexPath.defaultsKey) {
rows.append(ordering)
}
rows.sort(by: { [=12=].indexPath! < .indexPath! })
}
result[section] = rows
}
return result
}
}
错误代码:
*** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (CustomCellSwift.DataOrdering) for key (root); the class may be defined in source code or a library that is not linked'
JSON代码
func retrieveData() {
let getDataURL = "http://ip/test.org/Get.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 Test Object
let gID: String = (jsonArray[i] as AnyObject).object(forKey: "id") as! String
let gName: String = (jsonArray[i] as AnyObject).object(forKey: "gameName") as! String
// Add Test Objects to Test Array
testArray.append(Test(gameTest: tName, andTestID: tID))
}
}
catch {
print("Error: (Getting Data)")
}
myTableView.reloadData()
}
申请分类码
func applySorting(_ dataIdBlock: (Any) -> String) {
// get all saved ordering
guard let data = self.data else { return }
let ordering = DataHandling.allSavedOrdering(data.count)
var result: [[Any]] = [[], []]
for (section, ordering) in ordering {
guard section <= 1 else { continue } // make sure the section is 0 or 1
let rows = data.filter({ obj -> Bool in
return ordering.index(where: { [=15=].dataId == .some(dataIdBlock(obj)) }) != nil
})
result[section] = rows
}
self.items = result
}
您的 indexPath.defaultsKey
实施是错误的。
我看到你做了一个扩展:
(请注意以下是原问题中缺失的代码,不是我的解决方案)
extension IndexPath {
var defaultsKey: String {
return "data_ordering_\(section)_\(row)"
}
}
这一行 return "data_ordering_\(section)_\(row)"
就是问题所在。你有第二个 class DataOrdering
与此密钥一起存储。但你也想保存 DataHandling
。然后你需要一个不同的密钥。
您需要唯一的密钥来存储在 UserDefaults 中。
例如您应该将其更改为:
DataHandling(defaults: "DataHandling_"+ indexPath.defaultsKey)
更新
最好在 save(defaults box: String)
中添加 class 名称
defaults.set(savedData, forKey: "DataHandling_"+box)
还有 convenience init?(defaults box: String)
if let data = defaults.object(forKey: "DataHandling_"+box) as? Data,
(您也应该在您使用的其他 class 中更改此部分 (DataOrdering
) 以使保存的密钥对于您保存的每个不同 Class 都是唯一的
更新
如果您重命名了 class,那么只需重设 UserDefaults
就足够了。您可以通过从模拟器中删除应用程序来完成此操作,或者在您转到模拟器中的此菜单时从模拟器中删除所有时间数据:iOS Simulator > Reset Content and Settings
。但是为了不再出现错误,您不应该在没有当前 class 名称的情况下使用当前 IndexPath.defaultsKey
。
一个备注:
您可以一步保存一个数组到userdefaults。您不需要从 userdefaults 逐步读取每个对象。如果你保存一个数组,那么顺序是固定的(在我的例子中,这是相同的顺序)。我认为您应该 post 您的代码 https://codereview.stackexchange.com/ 以进一步优化您的代码
Swift3,
我在我的 iOS 应用程序中使用 NSUserDefaults 来保存和加载 indexPath 以及发生操作的行的哪个部分。因为我的 [=42= 中的每一行都有一个按钮]看法。在 ViewDidLoad.
中重新加载 table 时再次加载在 ViewDidLoad 中,我正在调用 fetch 函数,该函数应该保存和加载任何内容。
func fetchData() {
// request from remote or local
data = [testArray]
// Update the items to first section has 0 elements,
// and place all data in section 1
items = [[], data ?? []]
// apply ordering
applySorting() { "\([=10=])" }
// save ordering
saveSorting() { "\([=10=])" }
// refresh the table view
myTableView.reloadData()
}
在我的 buttonAction 中,我使用了 saveSorting() 函数。
func saveSorting(_ dataIdBlock: (Any) -> String) {
guard let items = self.items else { return }
for (section, rows) in items.enumerated() {
for (row, item) in rows.enumerated() {
let indexPath = IndexPath(row: row, section: section)
let dataId = dataIdBlock(item)
let ordering = DataHandling(dataId: dataId, indexPath: indexPath)
ordering.save(defaults: indexPath.defaultsKey)
}
}
}
这是我的断点图片,显示了日志以及它在代码中中断的位置。
非常感谢您帮助修复此崩溃。该应用程序甚至没有加载它在应用程序完全加载之前停留在白屏中。谢谢。
这是代码
class DataHandling: NSObject, NSCoding {
var indexPath: IndexPath?
var dataId: String?
init(dataId: String, indexPath: IndexPath) {
super.init()
self.dataId = dataId
self.indexPath = indexPath
}
required init(coder aDecoder: NSCoder) {
if let dataId = aDecoder.decodeObject(forKey: "dataId") as? String {
self.dataId = dataId
}
if let indexPath = aDecoder.decodeObject(forKey: "indexPath") as? IndexPath {
self.indexPath = indexPath
}
}
func encode(with aCoder: NSCoder) {
aCoder.encode(dataId, forKey: "dataId")
aCoder.encode(indexPath, forKey: "indexPath")
}
func save(defaults box: String) -> Bool {
let defaults = UserDefaults.standard
let savedData = NSKeyedArchiver.archivedData(withRootObject: self)
defaults.set(savedData, forKey: box)
return defaults.synchronize()
}
convenience init?(defaults box: String) {
let defaults = UserDefaults.standard
if let data = defaults.object(forKey: box) as? Data,
let obj = NSKeyedUnarchiver.unarchiveObject(with: data) as? DataHandling,
let dataId = obj.dataId,
let indexPath = obj.indexPath {
self.init(dataId: dataId, indexPath: indexPath)
} else {
return nil
}
}
class func allSavedOrdering(_ maxRows: Int) -> [Int: [DataHandling]] {
var result: [Int: [DataHandling]] = [:]
for section in 0...1 {
var rows: [DataHandling] = []
for row in 0..<maxRows {
let indexPath = IndexPath(row: row, section: section)
if let ordering = DataHandling(defaults: indexPath.defaultsKey) {
rows.append(ordering)
}
rows.sort(by: { [=12=].indexPath! < .indexPath! })
}
result[section] = rows
}
return result
}
}
错误代码:
*** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (CustomCellSwift.DataOrdering) for key (root); the class may be defined in source code or a library that is not linked'
JSON代码
func retrieveData() {
let getDataURL = "http://ip/test.org/Get.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 Test Object
let gID: String = (jsonArray[i] as AnyObject).object(forKey: "id") as! String
let gName: String = (jsonArray[i] as AnyObject).object(forKey: "gameName") as! String
// Add Test Objects to Test Array
testArray.append(Test(gameTest: tName, andTestID: tID))
}
}
catch {
print("Error: (Getting Data)")
}
myTableView.reloadData()
}
申请分类码
func applySorting(_ dataIdBlock: (Any) -> String) {
// get all saved ordering
guard let data = self.data else { return }
let ordering = DataHandling.allSavedOrdering(data.count)
var result: [[Any]] = [[], []]
for (section, ordering) in ordering {
guard section <= 1 else { continue } // make sure the section is 0 or 1
let rows = data.filter({ obj -> Bool in
return ordering.index(where: { [=15=].dataId == .some(dataIdBlock(obj)) }) != nil
})
result[section] = rows
}
self.items = result
}
您的 indexPath.defaultsKey
实施是错误的。
我看到你做了
(请注意以下是原问题中缺失的代码,不是我的解决方案)
extension IndexPath {
var defaultsKey: String {
return "data_ordering_\(section)_\(row)"
}
}
这一行 return "data_ordering_\(section)_\(row)"
就是问题所在。你有第二个 class DataOrdering
与此密钥一起存储。但你也想保存 DataHandling
。然后你需要一个不同的密钥。
您需要唯一的密钥来存储在 UserDefaults 中。
例如您应该将其更改为:
DataHandling(defaults: "DataHandling_"+ indexPath.defaultsKey)
更新
最好在 save(defaults box: String)
defaults.set(savedData, forKey: "DataHandling_"+box)
还有 convenience init?(defaults box: String)
if let data = defaults.object(forKey: "DataHandling_"+box) as? Data,
(您也应该在您使用的其他 class 中更改此部分 (DataOrdering
) 以使保存的密钥对于您保存的每个不同 Class 都是唯一的
更新
如果您重命名了 class,那么只需重设 UserDefaults
就足够了。您可以通过从模拟器中删除应用程序来完成此操作,或者在您转到模拟器中的此菜单时从模拟器中删除所有时间数据:iOS Simulator > Reset Content and Settings
。但是为了不再出现错误,您不应该在没有当前 class 名称的情况下使用当前 IndexPath.defaultsKey
。
一个备注:
您可以一步保存一个数组到userdefaults。您不需要从 userdefaults 逐步读取每个对象。如果你保存一个数组,那么顺序是固定的(在我的例子中,这是相同的顺序)。我认为您应该 post 您的代码 https://codereview.stackexchange.com/ 以进一步优化您的代码