简体   繁体   English

Swift 4:将自定义对象(带有嵌套的自定义对象)数组保存并检索到UserDefaults

[英]Swift 4: Save and retrieve an array of custom objects (with nested custom objects) to UserDefaults

I am trying to save an array of custom objects to UserDefaults. 我试图将自定义对象的数组保存到UserDefaults。 I believe I should, at this point, be saving correctly, but I continue to get the "unrecognized selector" error. 我相信我现在应该正确保存,但是我继续收到“无法识别的选择器”错误。 I think this may be because two parts of my custom object are arrays of other custom objects. 我认为这可能是因为我的自定义对象的两个部分是其他自定义对象的数组。 What am I doing wrong here? 我在这里做错了什么?

The Object: 物体:

class SaleObject: NSObject, NSCoding {

//MARK: Properties

var ranges: [RangeObject]
var timberSaleName: String
var lbOperatorName: String
var lbOperatorID: String
var timberSaleID: Int
var ID: Int
var contracts: [ContractObject]

//MARK: Initialization
init(ranges: [RangeObject], timberSaleName: String, lbOperatorName: String, lbOperatorID: String, timberSaleID: Int, ID: Int ,contracts: [ContractObject]) {

    //Initialize stored properties
    self.ranges = ranges
    self.timberSaleName = timberSaleName
    self.lbOperatorName = lbOperatorName
    self.lbOperatorID = lbOperatorID
    self.timberSaleID = timberSaleID
    self.ID = ID
    self.contracts = contracts

}

required init?(coder aDecoder: NSCoder) {
    self.ranges = (aDecoder.decodeObject(forKey: "ranges") as? [RangeObject])!
    self.timberSaleName = (aDecoder.decodeObject(forKey: "timber_sale_name") as? String)!
    self.lbOperatorName = (aDecoder.decodeObject(forKey: "lb_operator_name") as? String)!
    self.lbOperatorID = (aDecoder.decodeObject(forKey: "lb_operator_id") as? String)!
    self.timberSaleID = (aDecoder.decodeObject(forKey: "timber_sale_id") as? Int)!
    self.ID = (aDecoder.decodeObject(forKey: "id") as? Int)!
    self.contracts = (aDecoder.decodeObject(forKey: "contracts") as? [ContractObject])!
}

func encode(with aCoder: NSCoder) {
    aCoder.encode(self.ranges, forKey: "ranges")
    aCoder.encode(self.timberSaleName, forKey: "timber_sale_name")
    aCoder.encode(self.lbOperatorName, forKey: "lb_operator_name")
    aCoder.encode(self.lbOperatorID, forKey: "lb_operator_id")
    aCoder.encode(self.timberSaleID, forKey: "timber_sale_id")
    aCoder.encode(self.ID, forKey: "id")
    aCoder.encode(self.contracts, forKey: "contracts")
}

}

The save attempt: 保存尝试:

let userDefaults = UserDefaults.standard
        let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: self.saleArray)
        userDefaults.set(encodedData, forKey: "sales")

The error: 错误:

2018-11-28 14:28:30.024768-0600 Load BOSS[14991:282335] 
libMobileGestalt MobileGestalt.c:890: MGIsDeviceOneOfType is not 
supported on this platform.
2018-11-28 14:28:30.362954-0600 Load BOSS[14991:282335] [MC] System 
group container for systemgroup.com.apple.configurationprofiles path 
is /Users/beauburchfield/Library/Developer/
CoreSimulator/Devices/4ED03814-DD8C-43F3-ABA7- 
B5D0751161A0/data/Containers/Shared/SystemGroup/
systemgroup.com.apple.configurationprofiles
2018-11-28 14:28:30.364591-0600 Load BOSS[14991:282335] [MC] Reading 
from private effective user settings.
2018-11-28 14:28:31.034136-0600 Load BOSS[14991:282394] - 
[Load_BOSS.RangeObject encodeWithCoder:]: unrecognized selector sent 
 to 
instance 0x600001bcc680
2018-11-28 14:28:31.037429-0600 Load BOSS[14991:282394] *** 
Terminating app due to uncaught exception 
'NSInvalidArgumentException', reason: '-[Load_BOSS.RangeObject 
encodeWithCoder:]: unrecognized selector sent to instance 
0x600001bcc680'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010cbc51bb _ . 
_exceptionPreprocess + 331
1   libobjc.A.dylib                     0x000000010b73b735 
objc_exception_throw + 48
2   CoreFoundation                      0x000000010cbe3f44 - 
[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3   CoreFoundation                      0x000000010cbc9ed6 ___forwarding___ + 1446
4   CoreFoundation                      0x000000010cbcbda8 _CF_forwarding_prep_0 + 120
5   Foundation                          0x000000010b182175 _encodeObject + 1230
6   Foundation                          0x000000010b182aee -[NSKeyedArchiver _encodeArrayOfObjects:forKey:] + 439
7   Foundation                          0x000000010b182175 _encodeObject + 1230
8   Load BOSS                           0x000000010add0e2f $S9Load_BOSS10SaleObjectC6encode4withySo7NSCoderC_tF + 207
9   Load BOSS                           0x000000010add128c $S9Load_BOSS10SaleObjectC6encode4withySo7NSCoderC_tFTo + 60
10  Foundation                          0x000000010b182175 _encodeObject + 1230
11  Foundation                          0x000000010b182aee -[NSKeyedArchiver _encodeArrayOfObjects:forKey:] + 439
12  Foundation                          0x000000010b182175 _encodeObject + 1230
13  Foundation                          0x000000010b18122e +[NSKeyedArchiver archivedDataWithRootObject:] + 156
14  Load BOSS                           0x000000010adbf15a $S9Load_BOSS18MainViewControllerC14getJsonFromUrlyyFy10Foundation4DataVSg_So13NSURLResponseCSgs5Error_pSgtcfU_ + 15706
15  Load BOSS                           0x000000010adbf43d $S9Load_BOSS18MainViewControllerC14getJsonFromUrlyyFy10Foundation4DataVSg_So13NSURLResponseCSgs5Error_pSgtcfU_TA + 13
16  Load BOSS                           0x000000010adbf590 $S10Foundation4DataVSgSo13NSURLResponseCSgs5Error_pSgIegggg_So6NSDataCSgAGSo7NSErrorCSgIeyByyy_TR + 336
17  CFNetwork                           0x000000010e0d9940 __75-[__NSURLSessionLocal taskForClass:request:uploadFile:bodyData:completion:]_block_invoke + 19
18  CFNetwork                           0x000000010e0efb0c __49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke + 172
19  Foundation                          0x000000010b1a8f9e __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7
20  Foundation                          0x000000010b1a8ea5 -[NSBlockOperation main] + 68
21  Foundation                          0x000000010b1a5c14 -[__NSOperationInternal _start:] + 689
22  Foundation                          0x000000010b1abc4b __NSOQSchedule_f + 227
23  libdispatch.dylib                   0x000000010f0ae595 _dispatch_call_block_and_release + 12
24  libdispatch.dylib                   0x000000010f0af602 _dispatch_client_callout + 8
25  libdispatch.dylib                   0x000000010f0b254d _dispatch_continuation_pop + 565
26  libdispatch.dylib                   0x000000010f0b1927 _dispatch_async_redirect_invoke + 859
27  libdispatch.dylib                   0x000000010f0c000a _dispatch_root_queue_drain + 351
28  libdispatch.dylib                   0x000000010f0c09af _dispatch_worker_thread2 + 130
29  libsystem_pthread.dylib             0x000000010f49e70e _pthread_wqthread + 619
30  libsystem_pthread.dylib             0x000000010f49e435 start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException

NSCoding must be implemented for your two custom objects as well. 还必须为您的两个自定义对象实现NSCoding。 Everything else is fine. 其他一切都很好。

There is one thing I want to mention, though. 不过,我想提一件事。

(aDecoder.decodeObject(forKey: "timber_sale_name") as? String)!

can be written as 可以写成

aDecoder.decodeObject(forKey: "timber_sale_name") as! String

It basically means the same but is easier to read. 它的基本含义相同,但更易于阅读。

The crash here 这里的崩溃

[Load_BOSS.RangeObject encodeWithCoder:]: unrecognized selector sent [Load_BOSS.RangeObject encodeWithCoder:]:无法识别的选择器已发送

gives the reason which is making the root class conform to 给出使根类符合的原因

class SaleObject: NSObject, NSCoding {

doesn't mean that you can use custom object ( RangeObject in your case ) without making it also to conform like 并不意味着您可以使用自定义对象(在您的情况下为RangeObject )而无需使其也像

class RangeObject : NSObject, NSCoding {

Highly recommend using Codable 强烈建议使用Codable

class SaleObject: Codable { 

    let ranges: [RangeObject]
    let timberSaleName: String
    let lbOperatorName: String
    let lbOperatorID: String
    let timberSaleID: Int
    let ID: Int
    let contracts: [ContractObject]
}

class RangeObject : Codable { 
  // add it's properties 
}

How to use: 如何使用:

let data = try? JSONEncoder().encode(obj) // obj may be single SaleObject or array

let obj = try? JSONDecoder().decode(SaleObject.self,from:data) // for data being array make it [SaleObject].self

Go ahead and implement NSCoding for the other two custom objects. 继续为其他两个自定义对象实现NSCoding。 Also, change your decodeObject to decodeInteger on all Integers of your custom objects, and remove the "as! Int" from them. 另外,将您的自定义对象的所有Integer上的decodeObject更改为decodeInteger,然后从其中删除“ as!Int”。 Then, do this: 然后,执行以下操作:

let userDefaults = UserDefaults.standard
let encodedData = NSKeyedArchiver.archivedData(withRootObject: self.saleArray)
userDefaults.set(encodedData, forKey: "sales")

To retrieve the data, do this: 要检索数据,请执行以下操作:

let newArray = NSKeyedUnarchiver.unarchiveObject(with: data as! Data) as! [SaleObject]

After you have it working, go back and research Codable. 工作完成后,请回去研究Codable。 Enjoy! 请享用!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM