Попытка установить объект не-property-list как NSUserDefaults
Я думал, что знаю, что вызывает эту ошибку, но я не могу понять, что я сделал неправильно.
Вот полное сообщение об ошибке, которое я получаю:
Attempt to set a non-property-list object (
"<BC_Person: 0x8f3c140>"
) as an NSUserDefaults value for key personDataArray
У меня есть класс Person
, который, как мне кажется, соответствует протоколу NSCoding
, где у меня есть оба этих метода в моем классе person:
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.personsName forKey:@"BCPersonsName"];
[coder encodeObject:self.personsBills forKey:@"BCPersonsBillsArray"];
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
self.personsName = [coder decodeObjectForKey:@"BCPersonsName"];
self.personsBills = [coder decodeObjectForKey:@"BCPersonsBillsArray"];
}
return self;
}
В какой-то момент приложения NSString
в BC_PersonClass
установлен, и у меня есть класс DataSave
, который, я думаю, обрабатывает кодировку свойств в моем BC_PersonClass
.
Вот код, который я использую из класса DataSave
:
- (void)savePersonArrayData:(BC_Person *)personObject
{
// NSLog(@"name of the person %@", personObject.personsName);
[mutableDataArray addObject:personObject];
// set the temp array to the mutableData array
tempMuteArray = [NSMutableArray arrayWithArray:mutableDataArray];
// save the person object as nsData
NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:personObject];
// first add the person object to the mutable array
[tempMuteArray addObject:personEncodedObject];
// NSLog(@"Objects in the array %lu", (unsigned long)mutableDataArray.count);
// now we set that data array to the mutable array for saving
dataArray = [[NSArray alloc] initWithArray:mutableDataArray];
//dataArray = [NSArray arrayWithArray:mutableDataArray];
// save the object to NS User Defaults
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:dataArray forKey:@"personDataArray"];
[userData synchronize];
}
Надеюсь, этого достаточно кода, чтобы дать вам представление о том, что я пытаюсь сделать.
Снова я знаю, что моя проблема заключается в том, как я кодирую свои свойства в классе BC_Person, я просто не могу понять, что, хотя я делаю неправильно.
Спасибо за помощь!
Ответы
Ответ 1
Код, который вы отправили, пытается сохранить массив настраиваемых объектов до NSUserDefaults
. Вы не можете этого сделать. Внедрение методов NSCoding
не помогает. Вы можете хранить такие вещи, как NSArray
, NSDictionary
, NSString
, NSData
, NSNumber
и NSDate
в NSUserDefaults
.
Вам нужно преобразовать объект в NSData
(например, у вас есть в некотором коде) и сохранить его NSData
в NSUserDefaults
. Вы даже можете сохранить NSArray
NSData
, если вам нужно.
Когда вы читаете массив, вам нужно разблокировать NSData
, чтобы вернуть ваши объекты BC_Person
.
Возможно, вы хотите это:
- (void)savePersonArrayData:(BC_Person *)personObject {
[mutableDataArray addObject:personObject];
NSMutableArray *archiveArray = [NSMutableArray arrayWithCapacity:mutableDataArray.count];
for (BC_Person *personObject in mutableDataArray) {
NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:personObject];
[archiveArray addObject:personEncodedObject];
}
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:archiveArray forKey:@"personDataArray"];
}
Ответ 2
Мне кажется довольно расточительным пробегать массив и кодировать объекты в NSData самостоятельно. Ваша ошибка BC_Person is a non-property-list object
сообщает вам, что структура не знает, как сериализовать ваш объект person.
Итак, все, что необходимо, - это обеспечить, чтобы ваш объект-человек соответствовал NSCoding, тогда вы можете просто преобразовать свой массив настраиваемых объектов в NSData и сохранить его по умолчанию. Здесь есть детская площадка:
Изменить. Запись на NSUserDefaults
разбита на Xcode 7, чтобы игровая площадка архивировалась на данные и обратно и печатала результат. Шаг UserDefaults включается в случае, если он зафиксирован в более поздней точке
//: Playground - noun: a place where people can play
import Foundation
class Person: NSObject, NSCoding {
let surname: String
let firstname: String
required init(firstname:String, surname:String) {
self.firstname = firstname
self.surname = surname
super.init()
}
//MARK: - NSCoding -
required init(coder aDecoder: NSCoder) {
surname = aDecoder.decodeObjectForKey("surname") as! String
firstname = aDecoder.decodeObjectForKey("firstname") as! String
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(firstname, forKey: "firstname")
aCoder.encodeObject(surname, forKey: "surname")
}
}
//: ### Now lets define a function to convert our array to NSData
func archivePeople(people:[Person]) -> NSData {
let archivedObject = NSKeyedArchiver.archivedDataWithRootObject(people as NSArray)
return archivedObject
}
//: ### Create some people
let people = [Person(firstname: "johnny", surname:"appleseed"),Person(firstname: "peter", surname: "mill")]
//: ### Archive our people to NSData
let peopleData = archivePeople(people)
if let unarchivedPeople = NSKeyedUnarchiver.unarchiveObjectWithData(peopleData) as? [Person] {
for person in unarchivedPeople {
print("\(person.firstname), you have been unarchived")
}
} else {
print("Failed to unarchive people")
}
//: ### Lets try use NSUserDefaults
let UserDefaultsPeopleKey = "peoplekey"
func savePeople(people:[Person]) {
let archivedObject = archivePeople(people)
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(archivedObject, forKey: UserDefaultsPeopleKey)
defaults.synchronize()
}
func retrievePeople() -> [Person]? {
if let unarchivedObject = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaultsPeopleKey) as? NSData {
return NSKeyedUnarchiver.unarchiveObjectWithData(unarchivedObject) as? [Person]
}
return nil
}
if let retrievedPeople = retrievePeople() {
for person in retrievedPeople {
print("\(person.firstname), you have been unarchived")
}
} else {
print("Writing to UserDefaults is still broken in playgrounds")
}
И Voila, вы сохранили массив пользовательских объектов в NSUserDefaults
Ответ 3
Чтобы сохранить:
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:yourObject];
[currentDefaults setObject:data forKey:@"yourKeyName"];
Получить:
NSData *data = [currentDefaults objectForKey:@"yourKeyName"];
yourObjectType * token = [NSKeyedUnarchiver unarchiveObjectWithData:data];
Для удаления
[currentDefaults removeObjectForKey:@"yourKeyName"];
Ответ 4
Решение Swift 3
Простой класс утилиты
class ArchiveUtil {
private static let PeopleKey = "PeopleKey"
private static func archivePeople(people : [Human]) -> NSData {
return NSKeyedArchiver.archivedData(withRootObject: people as NSArray) as NSData
}
static func loadPeople() -> [Human]? {
if let unarchivedObject = UserDefaults.standard.object(forKey: PeopleKey) as? Data {
return NSKeyedUnarchiver.unarchiveObject(with: unarchivedObject as Data) as? [Human]
}
return nil
}
static func savePeople(people : [Human]?) {
let archivedObject = archivePeople(people: people!)
UserDefaults.standard.set(archivedObject, forKey: PeopleKey)
UserDefaults.standard.synchronize()
}
}
Класс модели
class Human: NSObject, NSCoding {
var name:String?
var age:Int?
required init(n:String, a:Int) {
name = n
age = a
}
required init(coder aDecoder: NSCoder) {
name = aDecoder.decodeObject(forKey: "name") as? String
age = aDecoder.decodeInteger(forKey: "age")
}
public func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(age, forKey: "age")
}
}
Как позвонить
var people = [Human]()
people.append(Human(n: "Sazzad", a: 21))
people.append(Human(n: "Hissain", a: 22))
people.append(Human(n: "Khan", a: 23))
ArchiveUtil.savePeople(people: people)
let others = ArchiveUtil.loadPeople()
for human in others! {
print("name = \(human.name!), age = \(human.age!)")
}
Ответ 5
Swift- 4 Xcode 9.1
попробуй этот код
вы не можете хранить маппер в NSUserDefault, вы можете хранить только NSData, NSString, NSNumber, NSDate, NSArray или NSDictionary.
let myData = NSKeyedArchiver.archivedData(withRootObject: myJson)
UserDefaults.standard.set(myData, forKey: "userJson")
let recovedUserJsonData = UserDefaults.standard.object(forKey: "userJson")
let recovedUserJson = NSKeyedUnarchiver.unarchiveObject(with: recovedUserJsonData)
Ответ 6
Во-первых, ответ rmaddy (выше) прав: реализация NSCoding
не помогает. Однако вам нужно реализовать NSCoding
для использования NSKeyedArchiver
, и все это, так что это просто еще один шаг... преобразование через NSData
.
Примеры методов
- (NSUserDefaults *) defaults {
return [NSUserDefaults standardUserDefaults];
}
- (void) persistObj:(id)value forKey:(NSString *)key {
[self.defaults setObject:value forKey:key];
[self.defaults synchronize];
}
- (void) persistObjAsData:(id)encodableObject forKey:(NSString *)key {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:encodableObject];
[self persistObj:data forKey:key];
}
- (id) objectFromDataWithKey:(NSString*)key {
NSData *data = [self.defaults objectForKey:key];
return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
Итак, вы можете обернуть ваши объекты NSCoding
в NSArray
или NSDictionary
или что-то еще...
Ответ 7
У меня возникла проблема с поиском словаря на NSUserDefaults
. Оказывается, это не спасет, поскольку в нем содержатся значения NSNull
. Поэтому я просто скопировал словарь в изменяемый словарь, удалил нули, а затем сохранил его в NSUserDefaults
NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary_trying_to_save];
[dictionary removeObjectForKey:@"NullKey"];
[[NSUserDefaults standardUserDefaults] setObject:dictionary forKey:@"key"];
В этом случае я знал, какие ключи могут быть NSNull
.
Ответ 8
https://developer.apple.com/reference/foundation/userdefaults
Объектом по умолчанию должен быть список свойств, то есть экземпляр (или для коллекций, комбинация экземпляров): NSData, NSString, NSNumber, NSDate, NSArray или NSDictionary.
Если вы хотите сохранить какой-либо объект другого типа, вы должны архивировать его, чтобы создать экземпляр NSData. Для получения дополнительной информации см. Руководство по программированию и настройкам.
Ответ 9
Свифт 5: Протокол Кодируемый может быть использован вместо NSKeyedArchiever.
struct User: Codable {
let id: String
let mail: String
let fullName: String
}
Структура Pref - это пользовательская оболочка вокруг стандартного объекта UserDefaults.
struct Pref {
static let keyUser = "Pref.User"
static var user: User? {
get {
if let data = UserDefaults.standard.object(forKey: keyUser) as? Data {
do {
return try JSONDecoder().decode(User.self, from: data)
} catch {
print("Error while decoding user data")
}
}
return nil
}
set {
if let newValue = newValue {
do {
let data = try JSONEncoder().encode(newValue)
UserDefaults.standard.set(data, forKey: keyUser)
} catch {
print("Error while encoding user data")
}
} else {
UserDefaults.standard.removeObject(forKey: keyUser)
}
}
}
}
Таким образом, вы можете использовать это так:
Pref.user?.name = "John"
if let user = Pref.user {...
Ответ 10
Swift 5 Очень простой способ
//MARK:- First you need to encoded your arr or what ever object you want to save in UserDefaults
//in my case i want to save Picture (NMutableArray) in the User Defaults in
//in this array some objects are UIImage & Strings
//first i have to encoded the NMutableArray
let encodedData = NSKeyedArchiver.archivedData(withRootObject: yourArrayName)
//MARK:- Array save in UserDefaults
defaults.set(encodedData, forKey: "YourKeyName")
//MARK:- When you want to retreive data from UserDefaults
let decoded = defaults.object(forKey: "YourKeyName") as! Data
yourArrayName = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! NSMutableArray
//MARK: Enjoy this arrry "yourArrayName"