如何使用swift将对象数组保存到NSUserDefault?

时间:2015-01-30 16:58:18

标签: ios swift nsuserdefaults

这是我定义的课程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")
    }

}

这就是我尝试保存Place s:

数组的方法
var placesArray = [Place]

//...

func savePlaces() {
    NSUserDefaults.standardUserDefaults().setObject(placesArray, forKey: "places")
    println("place saved")
}

它不起作用,这是我在控制台上得到的:

Property list invalid for format: 200 (property lists cannot contain objects of type 'CFType')

我是iOS新手,你能帮助我吗?

第二版

我找到了保存数据的解决方案:

func savePlaces(){
    let myData = NSKeyedArchiver.archivedDataWithRootObject(placesArray)
   NSUserDefaults.standardUserDefaults().setObject(myData, forKey: "places")
    println("place saved")
}

但是在使用以下代码加载数据时出现错误:

 let placesData = NSUserDefaults.standardUserDefaults().objectForKey("places") as? NSData

 if placesData != nil {
      placesArray = NSKeyedUnarchiver.unarchiveObjectWithData(placesData!) as [Place]
 }

错误是:

[NSKeyedUnarchiver decodeDoubleForKey:]: value for key (latitude) is not a double number'

我很确定我存档了Double,存储/加载过程存在问题

有任何线索吗?

8 个答案:

答案 0 :(得分:26)

来自Property List Programming Guide

  

如果属性列表对象是容器(即数组或字典),则其中包含的所有对象也必须是属性列表对象。如果数组或字典包含不是属性列表对象的对象,则无法使用各种属性列表方法和函数保存和还原数据层次结构。

您需要使用NSDataNSKeyedArchiver将对象转换为NSKeyedUnarchiver个实例。

例如:

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…
        }

    }
}

答案 1 :(得分:17)

Swift 3& 4

以下是Swift 3& S中的完整示例代码。 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)")
    }
}

使用示例:

savePlaces()
loadPlaces()

控制台输出:

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'

答案 2 :(得分:17)

Swift 4

我们需要序列化swift对象以将其保存到userDefaults

  

在swift 4中我们可以使用Codable协议,这使我们的序列化和JSON解析生活变得轻松

工作流程(在UserDefaults中保存swift对象):

  1. 将Codable协议确认为模型类(类Place:Codable)。
  2. 创建类的对象。
  3. 使用JsonEncoder类序列化该类。
  4. 将序列化(数据)对象保存到UserDefaults。
  5. 工作流程(从UserDefaults获取swift对象):

    1. 从UserDefaults获取数据(将返回序列化(数据)对象)
    2. 使用JsonDecoder类解码数据
    3. Swift 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
          }
      }
      

答案 3 :(得分:7)

您已准备好归档地点,但您现在假设在将其存储在NSUserDefaults中时,将自动归档一系列Place。它不会。 必须将其归档。错误消息告诉你这个。 NSUserDefaults中唯一可以保存的是具有属性列表类型的对象:NSArray,NSDictionary,NSString,NSData,NSDate和NSNumber。 Place对象不是其中之一。

不是直接尝试保存数组,而是存档它。现在它是一个NSData - 这是属性列表对象类型之一:

let myData = NSKeyedArchiver.archivedDataWithRootObject(placesArray)

现在您可以将myData存储在NSUserDefaults中,因为它是NSData。当然,当你再次将它拉出来时,你也必须将它取消归档,将它从NSData转回到Place数组中。

编辑:顺便说一下,作为事后的想法,我发现你的Place类可能必须明确采用NSCoding协议才能使其工作。你似乎已经省略了这一步。

答案 4 :(得分:6)

在Swift 4.0+中,我们可以使用由2个协议组成的类型别名Codable:Decodable&可编码

为方便起见,我创建了一个通用的解码和编码方法,其类型约束为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()
    }
}

用法 - 保存对象/数组/字典:

假设我们有一个自定义对象:

struct MyObject: Codable {
    let counter: Int
    let message: String
}

我们已经从中创建了一个实例:

let myObjectInstance = MyObject(counter: 10, message: "hi there")

使用上面的通用扩展,我们现在可以保存此对象,如下所示:

UserDefaults.standard.encode(for: myObjectInstance, using: String(describing: MyObject))

保存相同类型的数组:

UserDefaults.standard.encode(for:[myFirstObjectInstance, mySecondObjectInstance], using: String(describing: MyObject))

保存具有该类型的字典:

let dictionary = ["HashMe" : myObjectInstance]
UserDefaults.standard.encode(for: dictionary, using: String(describing: MyObject))

用法 - 加载对象/数组/字典:

加载单个对象:

let myDecodedObject = UserDefaults.standard.decode(for: MyObject.self, using: String(describing: MyObject))

加载相同类型的数组:

let myDecodedObject = UserDefaults.standard.decode(for: [MyObject].self, using: String(describing: MyObject))

加载具有该类型的字典:

let myDecodedObject = UserDefaults.standard.decode(for: ["HashMe" : myObjectInstance].self, using: String(describing: MyObject))

答案 5 :(得分:1)

以下是我使用的代码。希望这会有所帮助。

考虑体育是对象。首先,我们需要确认对象类的 Codable 类。

class Sport: Codable {
    var id: Int!
    var name: String!
}

然后,我们可以简单地使用以下代码通过UserDefaults保存对象的数组。

func saveSports(_ list:[Sport]) {
        UserDefaults.standard..set(try? PropertyListEncoder().encode(list), forKey:"KEY")
        UserDefaults.standard..synchronize()
    }

现在,我们可以简单地将对象列表作为参数传递并使用方法 saveSports()

保存

要获取保存的数据,我们可以使用以下代码。

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
}

方法 getSports()返回保存的数据。

答案 6 :(得分:1)

雨燕5

将以下属性放入您的一位经理中:

class UserSessionManager
{
    // MARK:- Properties

    public static var shared = UserSessionManager()

    var places: [Place]
    {
        get
        {
            guard let data = UserDefaults.standard.data(forKey: "places") else { return [] }
            return (try? JSONDecoder().decode([Place].self, from: data)) ?? []
        }
        set
        {
            let data = try? NSKeyedArchiver.archivedData(withRootObject: newValue, requiringSecureCoding: false)
            UserDefaults.standard.set(data, forKey: "places")
        }
    }

    // MARK:- Init

    private init(){}
}

使用

var placesArray: [Place] = []
placesArray.append(Place(latitude: 12, longitude: 21, name: "place 1"))

// save it easily!
UserSessionManager.shared.places = placesArray

// load it easily
let mySavedPlaces = UserSessionManager.shared.places

答案 7 :(得分:0)

“ Mobile Den”版本运行良好,但某些类型在12.0版本中已弃用。我刷新了这堂课。 SWIFT 5

Nan::TryCatch try_catch;
v8::Local<v8::String> leds_string = Nan::New<v8::String>("leds").ToLocalChecked();
Nan::MaybeLocal<v8::Value> maybe_leds = Nan::Get(options, leds_string);

v8::Local<v8::Value> leds;
if (!maybe_leds.ToLocal(&leds)) {
  // An exception was thrown while reading `options["leds"]`.
  // This is the same as `maybe_leds.IsEmpty() == true`.
  // It is also the same as `try_catch.HasCaught() == true`.
  return try_catch.ReThrow();
}
// Now follows your original code.
if (leds->IsUndefined()) {
  // The `options` object didn't have a `leds` property, or it was undefined.
  return Nan::ThrowTypeError("configure(): leds must be defined");
}

// Success case: all good.
ws2811.channel[0].count = Nan::To<int>(leds).FromMaybe(ws2811.channel[0].count);