简体   繁体   English

如何在 NSUserDefaults 中存储自定义对象

[英]How to store custom objects in NSUserDefaults

Alright, so I've been doing some poking around, and I realize my problem, but I don't know how to fix it.好的,所以我一直在四处摸索,我意识到我的问题,但我不知道如何解决它。 I have made a custom class to hold some data.我制作了一个自定义类来保存一些数据。 I make objects for this class, and I need to them to last between sessions.我为这个类制作对象,我需要它们在会话之间持续。 Before I was putting all my information in NSUserDefaults , but this isn't working.在我将所有信息放入NSUserDefaults ,但这不起作用。

-[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '<Player: 0x3b0cc90>' of class 'Player'.

That is the error message I get when I put my custom class, "Player", in the NSUserDefaults .这是我将自定义类“Player”放入NSUserDefaults时收到的错误消息。 Now, I've read up that apparently NSUserDefaults only stores some types of information.现在,我读到显然NSUserDefaults只存储某些类型的信息。 So how an I get my objects into NSUSerDefaults ?那么如何将我的对象放入NSUSerDefaults呢?

I read that there should be a way to to "encode" my custom object and then put it in, but I'm not sure how to implement it, help would be appreciated!我读到应该有一种方法可以“编码”我的自定义对象然后将其放入,但我不确定如何实现它,将不胜感激! Thank you!谢谢!

****EDIT**** ****编辑****

Alright, so I worked with the code given below (Thank you!), but I'm still having some issues.好的,所以我使用了下面给出的代码(谢谢!),但我仍然遇到一些问题。 Basically, the code crashes now and I'm not sure why, because it doesn't give any errors.基本上,代码现在崩溃了,我不知道为什么,因为它没有给出任何错误。 Perhaps I'm missing something basic and I'm just too tired, but we'll see.也许我错过了一些基本的东西,我太累了,但我们会看到的。 Here is the implementation of my Custom class, "Player":这是我的自定义类“播放器”的实现:

@interface Player : NSObject {
    NSString *name;
    NSNumber *life;
    //Log of player's life
}
//Getting functions, return the info
- (NSString *)name;
- (int)life;


- (id)init;

//These are the setters
- (void)setName:(NSString *)input; //string
- (void)setLife:(NSNumber *)input; //number    

@end

Implementation File:实施文件:

#import "Player.h"
@implementation Player
- (id)init {
    if (self = [super init]) {
        [self setName:@"Player Name"];
        [self setLife:[NSNumber numberWithInt:20]];
        [self setPsnCounters:[NSNumber numberWithInt:0]];
    }
    return self;
}

- (NSString *)name {return name;}
- (int)life {return [life intValue];}
- (void)setName:(NSString *)input {
    [input retain];
    if (name != nil) {
        [name release];
    }
    name = input;
}
- (void)setLife:(NSNumber *)input {
    [input retain];
    if (life != nil) {
        [life release];
    }
    life = input;
}
/* This code has been added to support encoding and decoding my objecst */

-(void)encodeWithCoder:(NSCoder *)encoder
{
    //Encode the properties of the object
    [encoder encodeObject:self.name forKey:@"name"];
    [encoder encodeObject:self.life forKey:@"life"];
}

-(id)initWithCoder:(NSCoder *)decoder
{
    self = [super init];
    if ( self != nil )
    {
        //decode the properties
        self.name = [decoder decodeObjectForKey:@"name"];
        self.life = [decoder decodeObjectForKey:@"life"];
    }
    return self;
}
-(void)dealloc {
    [name release];
    [life release];
    [super dealloc];
}
@end

So that's my class, pretty straight forward, I know it works in making my objects.这就是我的课程,非常直接,我知道它可以用于制作我的对象。 So here is the relevant parts of the AppDelegate file (where I call the encryption and decrypt functions):所以这里是 AppDelegate 文件的相关部分(我称之为加密和解密函数):

@class MainViewController;

@interface MagicApp201AppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    MainViewController *mainViewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) MainViewController *mainViewController;

-(void)saveCustomObject:(Player *)obj;
-(Player *)loadCustomObjectWithKey:(NSString*)key;


@end

And then the important parts of the implementation file:然后是实现文件的重要部分:

    #import "MagicApp201AppDelegate.h"
    #import "MainViewController.h"
    #import "Player.h"

    @implementation MagicApp201AppDelegate


    @synthesize window;
    @synthesize mainViewController;


    - (void)applicationDidFinishLaunching:(UIApplication *)application {
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
        //First check to see if some things exist
        int startup = [prefs integerForKey:@"appHasLaunched"];
        if (startup == nil) {
//Make the single player 
        Player *singlePlayer = [[Player alloc] init];
        NSLog([[NSString alloc] initWithFormat:@"%@\n%d\n%d",[singlePlayer name], [singlePlayer life], [singlePlayer psnCounters]]); //  test
        //Encode the single player so it can be stored in UserDefaults
        id test = [MagicApp201AppDelegate new];
        [test saveCustomObject:singlePlayer];
        [test release];
}
[prefs synchronize];
}

-(void)saveCustomObject:(Player *)object
{ 
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:object];
    [prefs setObject:myEncodedObject forKey:@"testing"];
}

-(Player *)loadCustomObjectWithKey:(NSString*)key
{
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    NSData *myEncodedObject = [prefs objectForKey:key ];
    Player *obj = (Player *)[NSKeyedUnarchiver unarchiveObjectWithData: myEncodedObject];
    return obj;
}

Eeee, sorry about all the code. Eeee,对不起所有的代码。 Just trying to help.只是想帮忙。 Basically, the app will launch and then crash immediatly.基本上,该应用程序将启动然后立即崩溃。 I've narrowed it down to the encryption part of the app, that's where it crashes, so I'm doing something wrong but I'm not sure what.我已经把它缩小到应用程序的加密部分,这就是它崩溃的地方,所以我做错了什么,但我不确定是什么。 Help would be appreciated again, thank you!再次感谢您的帮助,谢谢!

(I haven't gotten around to decrypting yet, as I haven't gotten encrypting working yet.) (我还没有开始解密,因为我还没有开始加密工作。)

On your Player class, implement the following two methods (substituting calls to encodeObject with something relevant to your own object):在您的 Player 类上,实现以下两个方法(用与您自己的对象相关的内容替换对 encodeObject 的调用):

- (void)encodeWithCoder:(NSCoder *)encoder {
    //Encode properties, other class variables, etc
    [encoder encodeObject:self.question forKey:@"question"];
    [encoder encodeObject:self.categoryName forKey:@"category"];
    [encoder encodeObject:self.subCategoryName forKey:@"subcategory"];
}

- (id)initWithCoder:(NSCoder *)decoder {
    if((self = [super init])) {
        //decode properties, other class vars
        self.question = [decoder decodeObjectForKey:@"question"];
        self.categoryName = [decoder decodeObjectForKey:@"category"];
        self.subCategoryName = [decoder decodeObjectForKey:@"subcategory"];
    }
    return self;
}

Reading and writing from NSUserDefaults :NSUserDefaults读取和写入:

- (void)saveCustomObject:(MyObject *)object key:(NSString *)key {
    NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:object];
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:encodedObject forKey:key];
    [defaults synchronize];

}

- (MyObject *)loadCustomObjectWithKey:(NSString *)key {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSData *encodedObject = [defaults objectForKey:key];
    MyObject *object = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject];
    return object;
}

Code shamelessly borrowed from: saving class in nsuserdefaults无耻地借用的代码: 在 nsuserdefaults 中保存类

Swift 4 introduced the Codable protocol which does all the magic for these kinds of tasks. Swift 4引入了Codable协议,它为这些类型的任务做了所有的魔法。 Just conform your custom struct/class to it:只需使您的自定义结构/类符合它:

struct Player: Codable {
  let name: String
  let life: Double
}

And for storing in the Defaults you can use the PropertyListEncoder/Decoder :对于存储在默认值中,您可以使用PropertyListEncoder/Decoder

let player = Player(name: "Jim", life: 3.14)
UserDefaults.standard.set(try! PropertyListEncoder().encode(player), forKey: kPlayerDefaultsKey)

let storedObject: Data = UserDefaults.standard.object(forKey: kPlayerDefaultsKey) as! Data
let storedPlayer: Player = try! PropertyListDecoder().decode(Player.self, from: storedObject)

It will work like that for arrays and other container classes of such objects too:它也适用于数组和此类对象的其他容器类:

try! PropertyListDecoder().decode([Player].self, from: storedArray)

I create a library RMMapper ( https://github.com/roomorama/RMMapper ) to help save custom object into NSUserDefaults easier and more convenient, because implementing encodeWithCoder and initWithCoder is super boring!我创建了一个库 RMMapper ( https://github.com/roomorama/RMMapper ) 来帮助更轻松、更方便地将自定义对象保存到 NSUserDefaults 中,因为实现 encodeWithCoder 和 initWithCoder 非常无聊!

To mark a class as archivable, just use: #import "NSObject+RMArchivable.h"要将类标记为可存档,只需使用: #import "NSObject+RMArchivable.h"

To save a custom object into NSUserDefaults:将自定义对象保存到 NSUserDefaults:

#import "NSUserDefaults+RMSaveCustomObject.h"
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults rm_setCustomObject:user forKey:@"SAVED_DATA"];

To get custom obj from NSUserDefaults:要从 NSUserDefaults 获取自定义 obj:

user = [defaults rm_customObjectForKey:@"SAVED_DATA"]; 

If anybody is looking for a swift version:如果有人正在寻找快速版本:

1) Create a custom class for your data 1)为您的数据创建一个自定义类

class customData: NSObject, NSCoding {
let name : String
let url : String
let desc : String

init(tuple : (String,String,String)){
    self.name = tuple.0
    self.url = tuple.1
    self.desc = tuple.2
}
func getName() -> String {
    return name
}
func getURL() -> String{
    return url
}
func getDescription() -> String {
    return desc
}
func getTuple() -> (String,String,String) {
    return (self.name,self.url,self.desc)
}

required init(coder aDecoder: NSCoder) {
    self.name = aDecoder.decodeObjectForKey("name") as! String
    self.url = aDecoder.decodeObjectForKey("url") as! String
    self.desc = aDecoder.decodeObjectForKey("desc") as! String
}

func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(self.name, forKey: "name")
    aCoder.encodeObject(self.url, forKey: "url")
    aCoder.encodeObject(self.desc, forKey: "desc")
} 
}

2) To save data use following function: 2) 要保存数据,请使用以下功能:

func saveData()
    {
        let data  = NSKeyedArchiver.archivedDataWithRootObject(custom)
        let defaults = NSUserDefaults.standardUserDefaults()
        defaults.setObject(data, forKey:"customArray" )
    }

3) To retrieve: 3) 检索:

if let data = NSUserDefaults.standardUserDefaults().objectForKey("customArray") as? NSData
        {
             custom = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! [customData]
        }

Note: Here I am saving and retrieving an array of the custom class objects.注意:这里我正在保存和检索自定义类对象的数组。

Taking @chrissr's answer and running with it, this code can be implemented into a nice category on NSUserDefaults to save and retrieve custom objects:接受@chrissr 的回答并运行它,可以将这段代码实现到NSUserDefaults上的一个很好的类别中,以保存和检索自定义对象:

@interface NSUserDefaults (NSUserDefaultsExtensions)

- (void)saveCustomObject:(id<NSCoding>)object
                     key:(NSString *)key;
- (id<NSCoding>)loadCustomObjectWithKey:(NSString *)key;

@end


@implementation NSUserDefaults (NSUserDefaultsExtensions)


- (void)saveCustomObject:(id<NSCoding>)object
                     key:(NSString *)key {
    NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:object];
    [self setObject:encodedObject forKey:key];
    [self synchronize];

}

- (id<NSCoding>)loadCustomObjectWithKey:(NSString *)key {
    NSData *encodedObject = [self objectForKey:key];
    id<NSCoding> object = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject];
    return object;
}

@end

Usage:用法:

[[NSUserDefaults standardUserDefaults] saveCustomObject:myObject key:@"myKey"];

Swift 3斯威夫特 3

class MyObject: NSObject, NSCoding  {
    let name : String
    let url : String
    let desc : String

    init(tuple : (String,String,String)){
        self.name = tuple.0
        self.url = tuple.1
        self.desc = tuple.2
    }
    func getName() -> String {
        return name
    }
    func getURL() -> String{
        return url
    }
    func getDescription() -> String {
        return desc
    }
    func getTuple() -> (String, String, String) {
        return (self.name,self.url,self.desc)
    }

    required init(coder aDecoder: NSCoder) {
        self.name = aDecoder.decodeObject(forKey: "name") as? String ?? ""
        self.url = aDecoder.decodeObject(forKey: "url") as? String ?? ""
        self.desc = aDecoder.decodeObject(forKey: "desc") as? String ?? ""
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(self.name, forKey: "name")
        aCoder.encode(self.url, forKey: "url")
        aCoder.encode(self.desc, forKey: "desc")
    }
    }

to store and retrieve:存储和检索:

func save() {
            let data  = NSKeyedArchiver.archivedData(withRootObject: object)
            UserDefaults.standard.set(data, forKey:"customData" )
        }
        func get() -> MyObject? {
            guard let data = UserDefaults.standard.object(forKey: "customData") as? Data else { return nil }
            return NSKeyedUnarchiver.unarchiveObject(with: data) as? MyObject
        }

Synchronize the data/object that you have saved into NSUserDefaults同步保存到 NSUserDefaults 的数据/对象

-(void)saveCustomObject:(Player *)object
{ 
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:object];
    [prefs setObject:myEncodedObject forKey:@"testing"];
    [prefs synchronize];
}

Hope this will help you.希望这会帮助你。 Thanks谢谢

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM