如何存储自定义对象在NSUserDefaults

好吧,我四处打听了一下,我意识到我的问题,但我不知道怎么解决。我已经做了一个自定义类来保存一些数据。我为这个类做对象,我需要它们在会话之间持续。在我把所有的信息放在NSUserDefaults之前,但这是不工作的。

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

这是当我将自定义类“Player”放在NSUserDefaults中时得到的错误消息。现在,我已经了解到NSUserDefaults显然只存储某些类型的信息。那么我如何将我的对象放入NSUSerDefaults呢?

我读到,应该有一种方法来“编码”我的自定义对象,然后把它放在,但我不知道如何实现它,帮助将感激!谢谢你!

* * * * * * * *进行编辑

好吧,所以我使用下面给出的代码(谢谢!),但我仍然有一些问题。基本上,代码现在崩溃了,我不确定为什么,因为它没有给出任何错误。也许我错过了一些基本的东西,我只是太累了,但我们会看到的。下面是我的自定义类“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

实现文件:

#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

这就是我的类,很简单,我知道它在创建对象时起作用。下面是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

然后是实现文件的重要部分:

    #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,抱歉写了这么多代码。只是想帮忙。基本上,应用程序会启动,然后立即崩溃。我已经把范围缩小到应用程序的加密部分,这就是它崩溃的地方,所以我做错了什么,但我不确定是什么。再次感谢您的帮助,谢谢!

(我还没有抽出时间解密,因为我还没有开始加密。)

142136 次浏览

在你的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;
}

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

代码无耻地借用:在nsuserdefaults中保存类

同步你已经保存到NSUserDefaults中的数据/对象

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

希望这对你有所帮助。谢谢

我创建了一个库RMMapper (https://github.com/roomorama/RMMapper)来帮助保存自定义对象到NSUserDefaults更容易和更方便,因为实现encodeWithCoder和initWithCoder是超级无聊!

要将一个类标记为可归档的,只需使用:#import "NSObject+RMArchivable.h"

将一个自定义对象保存到NSUserDefaults中:

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

从NSUserDefaults中获取自定义obj:

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

使用@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

用法:

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

如果有人在寻找一个快速的版本:

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)使用以下功能保存数据:

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

3)检索:

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

注意:这里我保存和检索自定义类对象的数组。

斯威夫特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")
}
}

存储和检索:

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
}

斯威夫特4引入了Codable协议,该协议为这类任务提供了所有的魔力。只需要使你的自定义结构/类符合它:

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

为了在默认值中存储,你可以使用PropertyListEncoder /译码器:

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)

对于数组和此类对象的其他容器类也可以这样工作:

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