Reputation: 1692
So I have an array of custom objects. When I try to save the array to NSUserDefaults, I have to archive them. Here is what I am doing to archive the array of my custom objects :
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//3
var allMessages = defaults.objectForKey(UserDefaultsMesssageKey) as! [AddMessageViewController.harassText]
var aMessage = AddMessageViewController.harassText(phoneNumber: 0, message: "1", frequency: 1, active: 1)
allMessages.append(aMessage)
saveMessage(allMessages)
return (allMessages.count)
}
func archiveMessage(message:[AddMessageViewController.harassText]) -> NSData {
let archivedObject = NSKeyedArchiver.archivedDataWithRootObject(message as NSArray)
return archivedObject
}
func saveMessage(messages: [AddMessageViewController.harassText]) {
let archivedObject = archiveMessage(messages)
defaults.setObject(archivedObject, forKey: UserDefaultsMesssageKey)
defaults.synchronize()
}
func retrieveData()-> [AddMessageViewController.harassText]? {
if let unarchiveObject = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaultsMesssageKey) as? NSData {
return NSKeyedUnarchiver.unarchiveObjectWithData(unarchiveObject) as? [AddMessageViewController.harassText]
}
return nil
}
The flow is :
On saveMessage call, pass the custom array in to get archived. Here is what the array looks like to GDB. You can see that it is an array, and it has 1 class object contained in it.
([Harass_Your_Kate.AddMessageViewController.harassText]) $R0 = 1 value {
[0] = 0x00007fa5ab409600 {
ObjectiveC.NSObject = {
isa = Harass_Your_Kate.AddMessageViewController.harassText
}
phoneNumber = 0
message = "1"
frequency = 1
active = 1
}
}
The array gets passed into archiveMessage. The code immediately fails on the NSKeyedArchiver method. The Error message is as follows :
[_TtCC16Harass_Your_Kate24AddMessageViewController10harassText encodeWithCoder:]: unrecognized selector sent to instance 0x7fa5ab409600
2016-07-15 16:31:26.019 Harass Your Kate[5858:7847495] * -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it. 2016-07-15 16:31:26.023 Harass Your Kate[5858:7847495] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_TtCC16Harass_Your_Kate24AddMessageViewController10harassText encodeWithCoder:]: unrecognized selector sent to instance 0x7fa5ab409600
I appreciate all of the help, thank you in advance!
EDIT: Here is the definition of my object:
class Text : NSObject {
var phoneNumber = 0 //Text address to send to
var message: String = "" //Message to be sent
var frequency = 24 //Number of hours between messages (usually a multiple of 24 - 24 = daily)
var active = 0 // 0 if cancelled, 1 if active
init(phoneNumber: Int, message: String, frequency: Int, active: Int ){
self.phoneNumber = phoneNumber
self.message = message
self.frequency = frequency
self.active = active
}
}
Upvotes: 3
Views: 2699
Reputation: 14824
NSKeyedArchiver
serializes objects by taking advantage of methods you have to provide by conforming to the NSCoding
protocol. It can't automatically infer how to save your custom classes; you have to implement that yourself as follows:
class Text : NSObject, NSCoding {
var phoneNumber = 0 //Text address to send to
var message: String = "" //Message to be sent
var frequency = 24 //Number of hours between messages (usually a multiple of 24 - 24 = daily)
var active = 0 // 0 if cancelled, 1 if active
init(phoneNumber: Int, message: String, frequency: Int, active: Int ){
self.phoneNumber = phoneNumber
self.message = message
self.frequency = frequency
self.active = active
}
required init?(coder aDecoder: NSCoder) {
phoneNumber = aDecoder.decodeInteger(forKey: "phoneNumber")
message = aDecoder.decodeObject(forKey: "message") as! String
frequency = aDecoder.decodeInteger(forKey: "frequency")
active = aDecoder.decodeInteger(forKey: "active")
}
func encode(with aCoder: NSCoder) {
aCoder.encode(phoneNumber, forKey: "phoneNumber")
aCoder.encode(message, forKey: "message")
aCoder.encode(frequency, forKey: "frequency")
aCoder.encode(active, forKey: "active")
}
}
As you can see, the initializer reads from the coder and creates and instance, whereas the encode
method saves relevant information to the coder. In a simple class like this, their implementations may seem obvious, but in a more complex custom type, there are non-trivial encoding decisions to make.
Upvotes: 3