簡體   English   中英

如何快速將對象數組保存到 NSUserDefault?

[英]How to save an array of objects to NSUserDefault with swift?

這是我定義的一個類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數組的方式:

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,保存/加載過程存在問題

任何線索?

斯威夫特 4

我們需要序列化我們的swift對象以將其保存到userDefaults

在 swift 4 中,我們可以使用 Codable 協議,這使我們的序列化和 JSON 解析變得容易

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

  1. 確認 Codable 協議到模型類(類 Place : Codable)。
  2. 創建類的對象。
  3. 使用 JsonEncoder 類序列化該類。
  4. 將序列化(數據)對象保存到 UserDefaults。

工作流程(從 UserDefaults 獲取 swift 對象):

  1. 從 UserDefaults 獲取數據(將返回 Serialized(Data) 對象)
  2. 使用 JsonDecoder 類解碼數據

斯威夫特 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
    }
}

屬性列表編程指南

如果屬性列表對象是容器(即數組或字典),則其中包含的所有對象也必須是屬性列表對象。 如果數組或字典包含不是屬性列表對象的對象,則無法使用各種屬性列表方法和函數來保存和恢復數據的層次結構。

您需要使用NSKeyedArchiverNSKeyedUnarchiver將對象與NSData實例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…
        }

    }
}

斯威夫特 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)")
    }
}

使用示例:

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'

在 Swift 4.0+ 中,我們可以使用類型別名Codable ,它由 2 個協議組成:Decodable 和 Encodable。

為方便起見,我創建了一個通用的 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)
    }
}

用法 - 保存對象/數組/字典:

假設我們有一個自定義對象:

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.self))

保存相同類型的數組:

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

使用該類型保存字典:

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

用法 - 加載對象/數組/字典:

加載單個對象:

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

加載相同類型的數組:

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

加載具有該類型的字典:

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

您已經准備好用於存檔的 Place,但是您現在假設當您將 Place 數組存儲在 NSUserDefaults 中時,它會自動存檔。 不會的。 必須將其存檔。 錯誤消息告訴您這一點。 唯一可以保存在 NSUserDefaults 中的是具有屬性列表類型的對象:NSArray、NSDictionary、NSString、NSData、NSDate 和 NSNumber。 Place 對象不是其中之一。

不要嘗試直接保存數組,而是將其存檔 現在它是一個 NSData——這是屬性列表對象類型之一:

let myData = NSKeyedArchiver.archivedDataWithRootObject(placesArray)

現在您可以將myData存儲在 NSUserDefaults 中,因為它是一個 NSData。 當然,當您再次將其拉出時,您還必須將其解壓縮以將其從 NSData 轉回 Place 數組。

編輯:順便說一句,我事后想到,您的 Place 類可能必須明確采用 NSCoding 協議才能使其正常工作。 你好像省略了這一步。

以下是我使用的代碼。 希望這會有所幫助。

考慮運動是對象。 我們需要將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()方法返回保存的數據。

斯威夫特 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
        {
            guard let data = try? JSONEncoder().encode(newValue) else { return }
            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

“Mobile Den”版本效果很好,但某些類型在 12.0 版本中已棄用。 我刷新了這個類。 快速 5

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"))

    do {
        let placesData = try NSKeyedArchiver.archivedData(withRootObject: placesArray, requiringSecureCoding: false)
        UserDefaults.standard.set(placesData, forKey: "places")
    } catch {
        print(error.localizedDescription)
    }
 
}

func loadPlaces() {
    
    guard let placesData = UserDefaults.standard.object(forKey: "places") as? NSData else {
        print("'places' not found in UserDefaults")
        return
    }
    
    do {
        guard let placesArray = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(placesData as Data) as? [Place] else { return }
        for place in placesArray {
            print("place.latitude: \(place.latitude)")
            print("place.longitude: \(place.longitude)")
            print("place.name: \(place.name)")
        }
    } catch {
        print(error.localizedDescription)
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM