web-dev-qa-db-ja.com

プロパティリスト以外のオブジェクトをNSUserDefaultsとして設定しようとしました

何がこのエラーの原因であるかはわかっていると思いましたが、何が間違っていたのかわからないようです。

これは私が得ている完全なエラーメッセージです。

キーpersonDataArray 
のNSUserDefaults値として、プロパティリスト以外のオブジェクト(
 "<BC_Person:0x8f3c140>" 
)を設定しようとしました。

私はPersonプロトコルに準拠していると思うNSCodingクラスを持っています。ここでは、私の個人クラスにこれらの両方のメソッドがあります。

- (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;
}

アプリのある時点で、BC_PersonClass内のNSStringが設定され、私はBC_PersonClass内のプロパティのエンコーディングを処理していると思うDataSaveクラスを持っています。これが私が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クラスで私のプロパティをどのようにエンコードしているかにあると私は認識しています。

助けてくれてありがとう!

174
icekomo

投稿したコードは、カスタムオブジェクトの配列をNSUserDefaultsに保存しようとしています。それはできません。 NSCodingメソッドを実装しても役に立ちません。 NSArrayNSDictionaryNSStringNSDataNSNumber、およびNSDateのようなもののみをNSUserDefaultsに格納できます。

オブジェクトをNSDataに変換し(一部のコードにあるように)、そのNSDataNSUserDefaultsに格納する必要があります。必要に応じて、NSArrayNSDataを格納することもできます。

あなたが配列を読み返すとき、あなたはあなたのBC_Personオブジェクトを取り戻すためにNSDataを展開する必要があります。

おそらくあなたはこれが欲しい:

- (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"];
}
257
rmaddy

自分で配列を調べてオブジェクトを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")
}

そして、ほら、あなたはNSUserDefaultsにカスタムオブジェクトの配列を保存しました

65
Daniel Galasko

保存する:

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"];
47
jose920405

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!)")
}
31

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

まず、rmaddyの答え(上)は正しいです。NSCodingを実装しても意味がありません。しかし、NSCodingを使用するにはNSKeyedArchiverを実装する必要があります。そのため、もう1つのステップがあります。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オブジェクトをNSArrayNSDictionaryなどでラップすることができます。

10
Dan Rosenstark

辞書をNSUserDefaultsに保存しようとすると、この問題が発生しました。 NSNullの値が含まれているため、保存できないことがわかりました。そのため、私は辞書を可変辞書にコピーしてnullを削除し、NSUserDefaultsに保存しました

NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary_trying_to_save];
[dictionary removeObjectForKey:@"NullKey"];
[[NSUserDefaults standardUserDefaults] setObject:dictionary forKey:@"key"];

この場合、どのキーがNSNullの値になるのかを知っていました。

5
simple_code

https://developer.Apple.com/reference/foundation/userdefaults

デフォルトオブジェクトは、プロパティリスト、つまりNSData、NSString、NSNumber、NSDate、NSArray、またはNSDictionaryのインスタンス(またはコレクションの場合はそのインスタンスの組み合わせ)である必要があります。

他の種類のオブジェクトを保存したい場合は、通常アーカイブしてNSDataのインスタンスを作成します。詳細については、Preferences and Settings Programming Guideを参照してください。

Swift 5:NSKeyedArchieverの代わりにCodableプロトコルを使用できます。

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 {...
1
Prcela

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"
0
Shakeel Ahmed