将自定义类对象转换为NSData

31

我有一个自定义类,想要将其保存到NSUserDefaults中。我被告知需要将该类对象转换为数据格式,以便保存到NSUserDefaults中。我找到了很多将离散字符串或整数转化为NSData的例子,但没有关于自定义类转化为NSData的内容。我对NSData编码等细节知之甚少,希望得到帮助。

编辑:虽然我知道这里有类似的答案,但是它们都不是使用Swift语言的。在两种语言之间进行翻译是可行的,但是非常繁琐,有时也非常反直觉。


1
可能是如何在NSUserDefaults中存储自定义对象的重复问题。 - Sunny Shah
1
你的类只需要符合NSCoding协议即可。 - Milan Agarwal
3个回答

49

这是一个简单的例子:

//Custom class.
class Person: NSObject, NSCoding {
    var name: String!
    var age: Int!
    required convenience init(coder decoder: NSCoder) {
        self.init()
        self.name = decoder.decodeObjectForKey("name") as! String
        self.age = decoder.decodeObjectForKey("age") as! Int
    }
    convenience init(name: String, age: Int) {
        self.init()
        self.name = name
        self.age = age
    }
    func encodeWithCoder(coder: NSCoder) {
        if let name = name { coder.encodeObject(name, forKey: "name") }
        if let age = age { coder.encodeObject(age, forKey: "age") }

    }
}

//create an instance of your custom class.
var newPerson = [Person]()

//add some values into custom class.
newPerson.append(Person(name: "Leo", age: 45))
newPerson.append(Person(name: "Dharmesh", age: 25))

//store you class object into NSUserDefaults.
let personData = NSKeyedArchiver.archivedDataWithRootObject(newPerson)
NSUserDefaults().setObject(personData, forKey: "personData")


//get your object from NSUserDefaults.
if let loadedData = NSUserDefaults().dataForKey("personData") {

    if let loadedPerson = NSKeyedUnarchiver.unarchiveObjectWithData(loadedData) as? [Person] {
        loadedPerson[0].name   //"Leo"
        loadedPerson[0].age    //45
    }
}

已在游乐场中测试。

希望这能有所帮助。


if let name = name 这个语法是什么意思?难道name不总是等于它本身吗? - johnbakers
1
@johnbakers 这确保了值不为空。 - businesscasual
@johnbakers,这主要是为了让你不必手动解包,因为你可以简单地进行nil检查,但是那样的话你就必须在大括号内部进行解包。if let会自动帮你解包。 - Fluidity
如果Person是一位集邮爱好者,因此拥有一个Stamp对象数组(包含年份、国家、价值等信息),那么如何处理自定义类中的自定义类数组呢? - Janneman
嗨@dharmesh,我正在尝试做同样的事情,但是我不是将数据存储在用户默认设置中,而是存储在文件中,但是当我检索数据时,我得到了空数据。有什么想法吗? - IamDev

7
以下示例代码基于Richie Rich的答案(请参见上文),并在以下环境中通过测试:
  • Xcode版本9.1(9B55)
  • Swift版本4.0.2(swiftlang-900.0.69.2 clang-900.0.38,目标:x86_64-apple-macosx10.9)
  • 配备macOS High Sierra(版本10.13.1)的MacBook Air(11英寸,2012年中期)

// Foundation is required to NSObject and NSCoding
import Foundation

// A custom class called Person with two properties (a string name and an
// integer age), that is a subclass of NSObject and adopts NSCoding protocol.
class Person: NSObject, NSCoding {
  var name: String!
  var age: Int!

  // The convenience initializer for class Person
  // Reference
  // https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-ID217
  convenience init(name: String, age: Int) {
    // self.init() is the designated initializer for class Person.
    // Reference
    // https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-ID219
    self.init()
    self.name = name
    self.age = age
  }

  // The initializer init(coder:) is required by NSCoding protocol
  // Reference
  // https://developer.apple.com/documentation/foundation/nscoding
  // https://developer.apple.com/documentation/foundation/nscoding/1416145-init
  required convenience init(coder aDecoder: NSCoder) {
    self.init()
    // as! is a type casting operator
    // Reference
    // https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID388
    self.name = aDecoder.decodeObject(forKey: "name") as! String
    self.age = aDecoder.decodeInteger(forKey: "age")
  }

  // The instance method encode(with:) is required by NSCoding protocol
  // Reference
  // https://developer.apple.com/documentation/foundation/nscoding
  // https://developer.apple.com/documentation/foundation/nscoding/1413933-encode
  func encode(with anEncoder: NSCoder) {
    if let name = name {
      anEncoder.encode(name, forKey: "name")
    }
    if let age = age {
      anEncoder.encode(age, forKey: "age")
    }
  }
}

// Create an array (or, generally speaking, a collection) as a container to
// hold instances of our custom class type Person.
// Reference
// https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html
var anArrayOfPersons = [Person]()
print(anArrayOfPersons.count) // 0

// Add two instances into anArrayOfPersons.
// Reference
// https://developer.apple.com/documentation/swift/array
// https://developer.apple.com/documentation/swift/array/1538872-append
anArrayOfPersons.append(Person(name: "Cong", age: 33))
anArrayOfPersons.append(Person(name: "Sunny", age: 2))

// Archive anArrayOfPersons into NSData using NSKeyedArchiver.
// Reference
// https://developer.apple.com/documentation/foundation/nskeyedarchiver
// https://developer.apple.com/documentation/foundation/nskeyedarchiver/1413189-archiveddata
let dataToSave = NSKeyedArchiver.archivedData(withRootObject: anArrayOfPersons)

// Persist data. Storing anArrayOfPersons into UserDefaults as data.
// Reference
// https://developer.apple.com/documentation/foundation/userdefaults
// https://developer.apple.com/documentation/foundation/userdefaults/1414067-set
UserDefaults().set(dataToSave, forKey: "tagOfData")

// Take our stored data (in previous step) from UserDefaults using the key
// "personData". Optional binding is used to make sure the retrieved data is
// not nil.
// Reference
// https://developer.apple.com/documentation/foundation/userdefaults
// https://developer.apple.com/documentation/foundation/userdefaults/1409590-data
// https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID333
if let dataRetrieved = UserDefaults().data(forKey: "tagOfData"),
  // Decode our instance objects from the retrieved data
  // Reference
  // https://developer.apple.com/documentation/foundation/nskeyedunarchiver
  // https://developer.apple.com/documentation/foundation/nskeyedunarchiver/1413894-unarchiveobject
  let anArrayOfPersonsRetrieved = NSKeyedUnarchiver.unarchiveObject(with: dataRetrieved) as? [Person] {
    // See how many bytes the data we retrieved has.
    print(dataRetrieved) // 393 bytes

    // See if the name and age properties are the same as what we stored.
    print(anArrayOfPersonsRetrieved[0].name) // "Cong"
    print(anArrayOfPersonsRetrieved[0].age)  // 45
    print(anArrayOfPersonsRetrieved[1].name) // "Sunny"
    print(anArrayOfPersonsRetrieved[1].age)  // 2
  }

2
这种回答格式应该是必需的。这教导人们如何使用文档,而不仅仅是告诉他们去查看文档。开发人员也需要学会如何使用那个工具。 - user-44651

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接