ios 如何快速将对象数组保存到 NSUserDefault?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28240848/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to save an array of objects to NSUserDefault with swift?
提问by Cherif
this is a class Place
I defined:
这是Place
我定义的一个类:
class Place: NSObject {
var latitude: Double
var longitude: Double
init(lat: Double, lng: Double, name: String){
self.latitude = lat
self.longitude = lng
}
required init(coder aDecoder: NSCoder) {
self.latitude = aDecoder.decodeDoubleForKey("latitude")
self.longitude = aDecoder.decodeDoubleForKey("longitude")
}
func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeObject(latitude, forKey: "latitude")
aCoder.encodeObject(longitude, forKey: "longitude")
}
}
This is how I tried to save an array of Place
s:
这就是我尝试保存Place
s数组的方式:
var placesArray = [Place]
//...
func savePlaces() {
NSUserDefaults.standardUserDefaults().setObject(placesArray, forKey: "places")
println("place saved")
}
It didn't work, this is what I get on the console:
它没有用,这是我在控制台上得到的:
Property list invalid for format: 200 (property lists cannot contain objects of type 'CFType')
I am new to iOS, could you help me ?
我是 iOS 新手,你能帮我吗?
SECOND EDITION
第二版
I found a solution to save the data :
我找到了保存数据的解决方案:
func savePlaces(){
let myData = NSKeyedArchiver.archivedDataWithRootObject(placesArray)
NSUserDefaults.standardUserDefaults().setObject(myData, forKey: "places")
println("place saved")
}
But I get an error when loading the data with this code :
但是使用此代码加载数据时出现错误:
let placesData = NSUserDefaults.standardUserDefaults().objectForKey("places") as? NSData
if placesData != nil {
placesArray = NSKeyedUnarchiver.unarchiveObjectWithData(placesData!) as [Place]
}
the error is :
错误是:
[NSKeyedUnarchiver decodeDoubleForKey:]: value for key (latitude) is not a double number'
I am pretty sure I archived a Double, there is an issue with the saving/loading process
我很确定我存档了一个 Double,保存/加载过程存在问题
Any clue ?
任何线索?
采纳答案by Aaron Brager
From the Property List Programming Guide:
从属性列表编程指南:
If a property-list object is a container (that is, an array or dictionary), all objects contained within it must also be property-list objects. If an array or dictionary contains objects that are not property-list objects, then you cannot save and restore the hierarchy of data using the various property-list methods and functions.
如果属性列表对象是容器(即数组或字典),则其中包含的所有对象也必须是属性列表对象。如果数组或字典包含不是属性列表对象的对象,则无法使用各种属性列表方法和函数来保存和恢复数据的层次结构。
You'll need to convert the object to and from an NSData
instance using NSKeyedArchiver
and NSKeyedUnarchiver
.
您需要NSData
使用NSKeyedArchiver
and将对象与实例相互转换NSKeyedUnarchiver
。
For example:
例如:
func savePlaces(){
let placesArray = [Place(lat: 123, lng: 123, name: "hi")]
let placesData = NSKeyedArchiver.archivedDataWithRootObject(placesArray)
NSUserDefaults.standardUserDefaults().setObject(placesData, forKey: "places")
}
func loadPlaces(){
let placesData = NSUserDefaults.standardUserDefaults().objectForKey("places") as? NSData
if let placesData = placesData {
let placesArray = NSKeyedUnarchiver.unarchiveObjectWithData(placesData) as? [Place]
if let placesArray = placesArray {
// do something…
}
}
}
回答by Kazi Abdullah Al Mamun
Swift 4
斯威夫特 4
We need to serialize our swift
object to save it into userDefaults
.
我们需要序列化我们的swift
对象以将其保存到userDefaults
.
In swift 4 we can use Codable protocol, which makes our life easy on serialization and JSON parsing
在 swift 4 中,我们可以使用 Codable 协议,这使我们的序列化和 JSON 解析变得容易
Workflow(Save swift object in UserDefaults):
工作流程(在 UserDefaults 中保存 swift 对象):
- Confirm Codable protocol to model class(class Place : Codable).
- Create object of class.
- Serialize that class using JsonEncoder class.
- Save serialized(Data) object to UserDefaults.
- 确认 Codable 协议到模型类(类 Place : Codable)。
- 创建类的对象。
- 使用 JsonEncoder 类序列化该类。
- 将序列化(数据)对象保存到 UserDefaults。
Workflow(Get swift object from UserDefaults):
工作流程(从 UserDefaults 获取 swift 对象):
- Get data from UserDefaults(Which will return Serialized(Data) object)
- Decode Data using JsonDecoder class
- 从 UserDefaults 获取数据(将返回 Serialized(Data) 对象)
- 使用 JsonDecoder 类解码数据
Swift 4 Code:
斯威夫特 4 代码:
class Place: Codable {
var latitude: Double
var longitude: Double
init(lat : Double, long: Double) {
self.latitude = lat
self.longitude = long
}
public static func savePlaces(){
var placeArray = [Place]()
let place1 = Place(lat: 10.0, long: 12.0)
let place2 = Place(lat: 5.0, long: 6.7)
let place3 = Place(lat: 4.3, long: 6.7)
placeArray.append(place1)
placeArray.append(place2)
placeArray.append(place3)
let placesData = try! JSONEncoder().encode(placeArray)
UserDefaults.standard.set(placesData, forKey: "places")
}
public static func getPlaces() -> [Place]?{
let placeData = UserDefaults.standard.data(forKey: "places")
let placeArray = try! JSONDecoder().decode([Place].self, from: placeData!)
return placeArray
}
}
回答by Mobile Dan
Swift 3 & 4
斯威夫特 3 & 4
The following is the complete example code in Swift 3 & 4.
以下是 Swift 3 & 4 中的完整示例代码。
import Foundation
class Place: NSObject, NSCoding {
var latitude: Double
var longitude: Double
var name: String
init(latitude: Double, longitude: Double, name: String) {
self.latitude = latitude
self.longitude = longitude
self.name = name
}
required init?(coder aDecoder: NSCoder) {
self.latitude = aDecoder.decodeDouble(forKey: "latitude")
self.longitude = aDecoder.decodeDouble(forKey: "longitude")
self.name = aDecoder.decodeObject(forKey: "name") as? String ?? ""
}
func encode(with aCoder: NSCoder) {
aCoder.encode(latitude, forKey: "latitude")
aCoder.encode(longitude, forKey: "longitude")
aCoder.encode(name, forKey: "name")
}
}
func savePlaces() {
var placesArray: [Place] = []
placesArray.append(Place(latitude: 12, longitude: 21, name: "place 1"))
placesArray.append(Place(latitude: 23, longitude: 32, name: "place 2"))
placesArray.append(Place(latitude: 34, longitude: 43, name: "place 3"))
let placesData = NSKeyedArchiver.archivedData(withRootObject: placesArray)
UserDefaults.standard.set(placesData, forKey: "places")
}
func loadPlaces() {
guard let placesData = UserDefaults.standard.object(forKey: "places") as? NSData else {
print("'places' not found in UserDefaults")
return
}
guard let placesArray = NSKeyedUnarchiver.unarchiveObject(with: placesData as Data) as? [Place] else {
print("Could not unarchive from placesData")
return
}
for place in placesArray {
print("")
print("place.latitude: \(place.latitude)")
print("place.longitude: \(place.longitude)")
print("place.name: \(place.name)")
}
}
Example Use:
使用示例:
savePlaces()
loadPlaces()
Console Output:
控制台输出:
place.latitude: 12.0
place.longitude: 21.0
place.name: 'place 1'
place.latitude: 23.0
place.longitude: 32.0
place.name: 'place 2'
place.latitude: 34.0
place.longitude: 43.0
place.name: 'place 3'
回答by OhadM
In Swift 4.0+ we can use the type alias Codablewhich consist of 2 protocols: Decodable & Encodable.
在 Swift 4.0+ 中,我们可以使用类型别名Codable,它由 2 个协议组成:Decodable 和 Encodable。
For convenience, I've created a generic decode and encode methods that are type constrained to Codable
:
为方便起见,我创建了一个通用的 decode 和 encode 方法,这些方法的类型限制为Codable
:
extension UserDefaults {
func decode<T : Codable>(for type : T.Type, using key : String) -> T? {
let defaults = UserDefaults.standard
guard let data = defaults.object(forKey: key) as? Data else {return nil}
let decodedObject = try? PropertyListDecoder().decode(type, from: data)
return decodedObject
}
func encode<T : Codable>(for type : T, using key : String) {
let defaults = UserDefaults.standard
let encodedData = try? PropertyListEncoder().encode(type)
defaults.set(encodedData, forKey: key)
defaults.synchronize()
}
}
Usage - saving an object/array/dictionary:
用法 - 保存对象/数组/字典:
Let's say we have a custom object:
假设我们有一个自定义对象:
struct MyObject: Codable {
let counter: Int
let message: String
}
and we have created an instance from it:
我们已经从中创建了一个实例:
let myObjectInstance = MyObject(counter: 10, message: "hi there")
Using the generic extension above we can now save this object as follow:
使用上面的通用扩展,我们现在可以按如下方式保存这个对象:
UserDefaults.standard.encode(for: myObjectInstance, using: String(describing: MyObject.self))
Saving an array of the same type:
保存相同类型的数组:
UserDefaults.standard.encode(for:[myFirstObjectInstance, mySecondObjectInstance], using: String(describing: MyObject.self))
Saving a dictionary with that type:
使用该类型保存字典:
let dictionary = ["HashMe" : myObjectInstance]
UserDefaults.standard.encode(for: dictionary, using: String(describing: MyObject.self))
Usage - loading an object/array/dictionary:
用法 - 加载对象/数组/字典:
Loading a single object:
加载单个对象:
let myDecodedObject = UserDefaults.standard.decode(for: MyObject.self, using: String(describing: MyObject.self))
Loading an array of the same type:
加载相同类型的数组:
let myDecodedObject = UserDefaults.standard.decode(for: [MyObject].self, using: String(describing: MyObject.self))
Loading a dictionary with that type:
加载具有该类型的字典:
let myDecodedObject = UserDefaults.standard.decode(for: ["HashMe" : myObjectInstance].self, using: String(describing: MyObject.self))
回答by matt
You have prepared Place for archiving, but you are now assuming that an array of Place will be archived automatically as you store it in NSUserDefaults. It won't be. Youhave to archive it. The error message is telling you this. The only things that can be saved in NSUserDefaults are objects with property list types: NSArray, NSDictionary, NSString, NSData, NSDate and NSNumber. A Place object is not one of those.
您已经准备好用于存档的 Place,但是您现在假设当您将 Place 数组存储在 NSUserDefaults 中时,它会自动存档。不会的。您必须将其存档。错误消息告诉您这一点。唯一可以保存在 NSUserDefaults 中的是具有属性列表类型的对象:NSArray、NSDictionary、NSString、NSData、NSDate 和 NSNumber。Place 对象不是其中之一。
Instead of trying to save the array directly, archiveit. Now it is an NSData — and that is one of the property list object types:
不要尝试直接保存数组,而是将其存档。现在它是一个 NSData——这是属性列表对象类型之一:
let myData = NSKeyedArchiver.archivedDataWithRootObject(placesArray)
Now you can store myData
in NSUserDefaults, because it is an NSData. Of course when you pull it out again you will also have to unarchive it to turn it from an NSData back into an array of Place.
现在您可以存储myData
在 NSUserDefaults 中,因为它是一个 NSData。当然,当您再次将其拉出时,您还必须将其解压缩以将其从 NSData 转回 Place 数组。
EDIT: By the way, it occurs to me, as an afterthought, that your Place class may have to explicitly adopt the NSCoding protocol to get this to work. You seem to have omitted that step.
编辑:顺便说一句,我事后想到,您的 Place 类可能必须明确采用 NSCoding 协议才能使其正常工作。你好像省略了这一步。
回答by Rahul Kavungal
Following is the code I use. Hope this will help.
以下是我使用的代码。希望这会有所帮助。
Consider Sportis the object. we need to confirm Codableclass to the object class as below.
考虑运动是对象。我们需要将Codable类确认为对象类,如下所示。
class Sport: Codable {
var id: Int!
var name: String!
}
Then we could simply use the following code to save the array of the object using UserDefaults.
然后我们可以简单地使用以下代码来保存使用 UserDefaults 的对象数组。
func saveSports(_ list:[Sport]) {
UserDefaults.standard.set(try? PropertyListEncoder().encode(list), forKey:"KEY")
UserDefaults.standard.synchronize()
}
Now we can simply pass the list of objects as a parameter and save using method saveSports()
现在我们可以简单地将对象列表作为参数传递并使用saveSports()方法保存
For fetching the saved data, we could use the following code.
为了获取保存的数据,我们可以使用以下代码。
func getSports() -> [Sport]? {
if let data = UserDefaults.standard.value(forKey:"KEY") as? Data {
let decodedSports = try? PropertyListDecoder().decode([Sport].self, from: data)
return decodedSports
}
return nil
}
The method getSports()returns the saved data.
getSports()方法返回保存的数据。