How to store custom objects in NSUserDefaults
好吧,所以我一直在四处摸索,我意识到我的问题,但我不知道如何解决。我创建了一个自定义类来保存一些数据。我为这个类创建对象,我需要它们在会话之间持续。在我把我所有的信息放进
1 | -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '<Player: 0x3b0cc90>' of class 'Player'. |
这是我在
我读到应该有一种方法来"编码"我的自定义对象,然后把它放进去,但我不确定如何实现它,将感谢您的帮助!谢谢您!
***编辑***
好吧,所以我使用下面给出的代码(谢谢!)但我还是有些问题。基本上,代码现在崩溃了,我不知道为什么,因为它没有给出任何错误。也许我错过了一些基本的东西,我只是太累了,但我们会看到的。下面是我的自定义类"player"的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @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 |
实施文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | #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文件的相关部分(在这里我调用加密和解密函数):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @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 |
然后是实现文件的重要部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #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:@"%@ %d %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; } |
Eee,对不起,所有的代码。只是想帮忙。基本上,应用程序将启动,然后立即崩溃。我已经把范围缩小到应用程序的加密部分,这就是它崩溃的地方,所以我做了一些错误的事情,但我不确定是什么。再次感谢您的帮助,谢谢!
(我还没有解密,因为我还没有加密工作。)
在您的播放器类别中,实现以下两种方法(用与您自己的对象相关的某种编码代替呼叫):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | - (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
ZZU1
代码从:拯救阶级
我创建了一个图书馆RMMAPPER(HTTPS://github.com/roomorama/RMMAPER)以帮助将用户对象保存到Nuserdefaults easier and more convenient,因为执行编码和initwithcoder是超级博灵!
标记一个可存档的类别,只需使用:
To save a custom object into nsuserdefaults:
1 2 3 | #import"NSUserDefaults+RMSaveCustomObject.h" NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; [defaults rm_setCustomObject:user forKey:@"SAVED_DATA"]; |
To get custom obj from Nsuserdefaults:
1 | user = [defaults rm_customObjectForKey:@"SAVED_DATA"]; |
SWIFT 4
引入可编译的协议,为这些任务提供所有的魔法。按照你的习惯结构/阶级:
1 2 3 4 | struct Player: Codable { let name: String let life: Double } |
在缺陷中存储您可以使用Propertylistencoder/Decoder:
1 2 3 4 5 | 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 classions of such objects too:
1 | try! PropertyListDecoder().decode([Player].self, from: storedArray) |
如果任何人都在寻找一个滑稽的版本
为您的数据创建一个自定义的分类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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:
1 2 3 4 5 6 | func saveData() { let data = NSKeyedArchiver.archivedDataWithRootObject(custom) let defaults = NSUserDefaults.standardUserDefaults() defaults.setObject(data, forKey:"customArray" ) } |
(3)追查:
1 2 3 4 | if let data = NSUserDefaults.standardUserDefaults().objectForKey("customArray") as? NSData { custom = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! [customData] } |
注:在这里,我保存并检索了一个基于习惯类物体的阵列。
Taking@chrissr's answer and running with it,this code can be implemented into a nice category on
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | @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 |
惯例:
1 | [[NSUserDefaults standardUserDefaults] saveCustomObject:myObject key:@"myKey"]; |
SWIFT 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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:
1 2 3 4 5 6 7 8 | 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 } |
同步数据/物件,您已存储在错误中
1 2 3 4 5 6 7 | -(void)saveCustomObject:(Player *)object { NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:object]; [prefs setObject:myEncodedObject forKey:@"testing"]; [prefs synchronize]; } |
希望这能帮助你。谢谢