गैर-संपत्ति-सूची ऑब्जेक्ट को NSUserDefaults के रूप में सेट करने का प्रयास करें


196

मुझे लगा कि मुझे पता है कि इस त्रुटि के कारण क्या था, लेकिन मैं यह पता नहीं लगा सकता कि मैंने क्या गलत किया।

यहाँ पूर्ण त्रुटि संदेश मिल रहा है:

गैर-संपत्ति-सूची ऑब्जेक्ट सेट करने का प्रयास करें (
   "<BC_Person: 0x8f3c140>"
) प्रमुख व्यक्तिडेटा के लिए एक NSUserDefaults मान के रूप में

मेरे पास एक 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;
}

अनुप्रयोग में कुछ बिंदु पर, 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];
}

मुझे आशा है कि यह आपको एक ओ कोड देने के लिए पर्याप्त है कि मैं क्या करने की कोशिश कर रहा हूं। फिर से मुझे पता है कि मेरी समस्या यह है कि मैं अपने बीसी_पर्सन वर्ग में अपनी संपत्तियों को कैसे कूट रहा हूं, मैं अभी यह पता नहीं लगा सकता कि क्या मैं गलत हूं।

सहायता के लिए धन्यवाद!


आश्चर्य है कि हम कैसे जांच सकते हैं कि यह संपत्ति सूची ऑब्जेक्ट है या नहीं
onmyway133

that I think is conforming to the NSCoding protocolइसके लिए इकाई परीक्षण जोड़ना सुपर आसान है, और वास्तव में इसके लायक है।
rr1g0

अपने मापदंडों की जांच करने के लिए सबसे अच्छा है। मुझे पता चला कि मैं एक स्ट्रिंग जोड़ रहा था जो एक नंबर था। इसलिए इसका उपयोग नहीं किया जाना था, इसलिए समस्या थी।
राजल

जवाबों:


272

आपके द्वारा पोस्ट किया गया कोड कस्टम ऑब्जेक्ट्स की एक सरणी को सहेजने की कोशिश करता है 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"];
}

एक प्रश्न मेरे पास है। अगर मैं एक सरणी में personEncodedObject जोड़ना चाहता था और फिर उपयोगकर्ता डेटा में सरणी डाल सकता था ... क्या मैं बस प्रतिस्थापित कर सकता था: [पुरालेख addObject: personEncodedObject]; एक NSArray के साथ और addObject जोड़ें: personEncodedObject को उस एरे में और फिर यूजरटाटा में सेव करें? अगर तुम मेरे कहने का पालन करो।
आइसकोमो

है ना? मुझे लगता है कि आप एक टाइपो बनाते हैं क्योंकि आप कोड की एक पंक्ति को उसी कोड के साथ बदलना चाहते हैं। मेरा कोड उपयोगकर्ता डिफॉल्ट में एन्कोडेड ऑब्जेक्ट्स की सरणी डालता है।
rmaddy

मुझे लगता है कि मैं हार गया हूं क्योंकि मुझे लगा कि आप केवल NSDrray का उपयोग userDefaults के साथ कर सकते हैं, लेकिन मैं आपके उदाहरण में देखता हूं कि आप NSMutable array का उपयोग कर रहे हैं। शायद मुझे कुछ समझ में नहीं आ रहा है ...
icekomo

2
NSMutableArrayफैली हुई है NSArray। यह पूरी तरह से ठीक है NSMutableArrayकि किसी भी विधि को पास करने की उम्मीद है जो एक की उम्मीद करता है NSArray। ध्यान रखें कि NSUserDefaultsजब आप इसे वापस पढ़ेंगे तो वास्तव में संग्रहीत सरणी अपरिवर्तनीय होगी।
rmaddy

1
क्या इससे प्रदर्शन में कुछ खर्च होता है? उदाहरण के लिए यदि यह एक लूप के माध्यम से चल रहा था और कई बार एक कस्टम क्लास ऑब्जेक्ट को पॉप्युलेट कर रहा था, तो इसे हर एक सरणी में जोड़ने से पहले NSData में बदल दिया जाए, क्या यह सामान्य डेटा प्रकारों को सरणी में पास करने की तुलना में कोई बड़ा प्रदर्शन मुद्दा होगा?
Rob85

66

यह सरणी के माध्यम से चलने और एनएसडीटा में वस्तुओं को अपने आप में एनकोड करने के बजाय मुझे बेकार लगता है। आपकी त्रुटि BC_Person is a non-property-list objectआपको बता रही है कि फ्रेमवर्क नहीं जानता कि आपके व्यक्ति ऑब्जेक्ट को कैसे क्रमांकित किया जाए।

इतना सब जरूरी है कि यह सुनिश्चित किया जाए कि आपकी व्यक्ति वस्तु NSCoding के अनुरूप हो, तो आप बस अपने कस्टम ऑब्जेक्ट्स को NSData में परिवर्तित कर सकते हैं और उस डिफॉल्ट को स्टोर कर सकते हैं। एक खेल का मैदान:

संपादित करें : लेखन NSUserDefaultsXcode 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 में कस्टम ऑब्जेक्ट की एक सरणी संग्रहीत की है


यह उत्तर ठोस है! मेरे पास मेरी वस्तुएं NSCoder के अनुरूप थीं, लेकिन मैं उस सरणी के बारे में भूल गया जहाँ वे संग्रहीत थे। धन्यवाद, मुझे कई घंटे बचाया!
मिकेल हेलमैन

1
@ डैनियल, मैंने आपका कोड सिर्फ खेल के मैदान xcode 7.3 में चिपकाया है और यह "पुनः प्राप्त करें लोगों पर एक त्रुटि दे रहा है: [व्यक्ति] = पुनः प्राप्त करें ()!" -> EXC_BAD_INSTRUCTION (कोड = EXC_I386_INVOP, सबकोड = 0x0)। मुझे क्या समायोजन करने की आवश्यकता है? धन्यवाद
rockhammer

1
@rockhammer NSUserDefaults की तरह खेल के मैदानों में काम नहीं करता है क्योंकि Xcode 7 :( शीघ्र ही अपडेट होगा
डैनियल गैलास्को

1
@ डैनियल, कोई बात नहीं। मैंने आगे बढ़कर अपना कोड मेरे प्रोजेक्ट में डाला और यह एक आकर्षण की तरह काम करता है! मेरे ऑब्जेक्ट क्लास में 3 स्ट्रिंग प्रकारों के अलावा एक NSDate है। मैं सिर्फ डिकोड दुर्गंध में स्ट्रिंग के लिए NSDate स्थानापन्न करने की जरूरत है। धन्यवाद
rockhammer

स्विफ्ट 3 के लिए:func encode(with aCoder: NSCoder)
अशोक

51

बचाना:

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

34

स्विफ्ट 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!)")
}

1
अच्छा! : D सबसे अच्छा स्विफ्ट अनुकूलन + +1
गैबो कुआड्रोस कर्डेनस

developer.apple.com/documentation/foundation/userdefaults/… ... "यह विधि अनावश्यक है और इसका उपयोग नहीं किया जाना चाहिए।" LOL ... सेब का कोई व्यक्ति टीम का खिलाड़ी नहीं है।
Nerdy Bunz

12

स्विफ्ट- 4 एक्सकोड 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)

8
इसके लिए शुक्रिया। NSKeyedArchiver.archivedData (withRootObject :) iOS12 में एक नई विधि में अपदस्थ किया गया है जो एक त्रुटि फेंकता है। शायद अपने कोड के लिए एक अद्यतन? :)
मर्सी

10

सबसे पहले, रैम्डी का उत्तर (ऊपर) सही है: कार्यान्वयन 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या जो कुछ भी ...


6

मैं इस समस्या को एक शब्दकोश को बचाने की कोशिश कर रहा था NSUserDefaults। यह पता चला है कि यह नहीं बचा होगा क्योंकि इसमें NSNullमूल्य निहित थे । इसलिए मैंने सिर्फ शब्दकोश को एक म्यूट किए गए शब्दकोश में कॉपी किया, फिर हटाए गए नल को हटा दियाNSUserDefaults

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

इस मामले में मुझे पता था कि कौन सी कुंजी NSNullमान हो सकती है।


4

स्विफ्ट 5: NSKeyedArchiever के बजाय कोडेबल प्रोटोकॉल का उपयोग किया जा सकता है ।

struct User: Codable {
    let id: String
    let mail: String
    let fullName: String
}

प्राथ struct 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
if let data = UserDefaults.standard.data(forKey: keyUser) {और Btw से कास्टिंग Userके लिए Userव्यर्थ बस हैreturn try JSONDecoder().decode(User.self, from: data)
सिंह Dabus

4

के साथ स्विफ्ट@propertyWrapper

Codableवस्तु को बचाओUserDefault

@propertyWrapper
    struct UserDefault<T: Codable> {
        let key: String
        let defaultValue: T

        init(_ key: String, defaultValue: T) {
            self.key = key
            self.defaultValue = defaultValue
        }

        var wrappedValue: T {
            get {

                if let data = UserDefaults.standard.object(forKey: key) as? Data,
                    let user = try? JSONDecoder().decode(T.self, from: data) {
                    return user

                }

                return  defaultValue
            }
            set {
                if let encoded = try? JSONEncoder().encode(newValue) {
                    UserDefaults.standard.set(encoded, forKey: key)
                }
            }
        }
    }




enum GlobalSettings {

    @UserDefault("user", defaultValue: User(name:"",pass:"")) static var user: User
}

उदाहरण उपयोगकर्ता मॉडल कोडेबल की पुष्टि करता है

struct User:Codable {
    let name:String
    let pass:String
}

इसे कैसे उपयोग करे

//Set value 
 GlobalSettings.user = User(name: "Ahmed", pass: "Ahmed")

//GetValue
print(GlobalSettings.user)

यह सबसे अच्छा आधुनिक उत्तर है। स्टेकर, इस उत्तर का उपयोग वास्तव में स्केलेबल है।
स्टीफन वासिलजेविक

3

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

एक डिफ़ॉल्ट ऑब्जेक्ट एक संपत्ति सूची होनी चाहिए- यानी, (या संग्रह के लिए, उदाहरणों का एक संयोजन) का एक उदाहरण: NSData, NSString, NSNumber, NSDate, NSArray, या NSDictionary।

यदि आप किसी अन्य प्रकार की वस्तु को संग्रहित करना चाहते हैं, तो आपको आमतौर पर NSData का उदाहरण बनाने के लिए इसे संग्रहित करना चाहिए। अधिक जानकारी के लिए, प्राथमिकताएँ और सेटिंग्स प्रोग्रामिंग गाइड देखें।


3

स्विफ्ट 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"

यह केवल कोड करने योग्य संरचना के लिए है
किशोर कुमार

मुझे मिलता है: 'archivedData (withRootObject :)' को MacOS में चित्रित किया गया था 10.14: Use + archivedDataWithRootObject: requiringSecureCoding: error: इसके बजाय
मल्टीटाट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.