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

Ios 如何在NSUserDefaults中存储自定义对象,ios,objective-c,iphone,encoding,nsuserdefaults,Ios,Objective C,Iphone,Encoding,Nsuserdefaults,好吧,我一直在做一些调查,我意识到我的问题,但我不知道如何解决它。我创建了一个自定义类来保存一些数据。我为这个类创建对象,我需要使它们在会话之间持续。在我将所有信息放入NSUserDefaults之前,但这不起作用 -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '<Player: 0x3b0cc90>' of class 'Player'. 实施文件: #import "Playe

好吧,我一直在做一些调查,我意识到我的问题,但我不知道如何解决它。我创建了一个自定义类来保存一些数据。我为这个类创建对象,我需要使它们在会话之间持续。在我将所有信息放入
NSUserDefaults
之前,但这不起作用

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

#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文件的相关部分(我在这里调用加密和解密函数):

Eeee,对所有的代码感到抱歉。只是想帮忙。基本上,该应用程序将启动,然后立即崩溃。我已经把它缩小到应用程序的加密部分,这就是它崩溃的地方,所以我做错了什么,但我不确定是什么。再次感谢您的帮助,谢谢


(我还没有开始解密,因为我还没有开始加密。)

在您的Player类上,实现以下两种方法(用与您自己的对象相关的内容替换对encodeObject的调用):

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中的数据/对象

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

希望这对你有帮助。谢谢

我创建了一个库RMMapper(),以帮助将自定义对象保存到NSUserDefaults中,这更容易、更方便,因为实现encodeWithCoder和initWithCoder非常无聊

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

要将自定义对象保存到NSUserDefaults中,请执行以下操作:

#import "NSUserDefaults+RMSaveCustomObject.h"
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults rm_setCustomObject:user forKey:@"SAVED_DATA"];
user = [defaults rm_customObjectForKey:@"SAVED_DATA"]; 
要从NSUserDefaults获取自定义obj,请执行以下操作:

#import "NSUserDefaults+RMSaveCustomObject.h"
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults rm_setCustomObject:user forKey:@"SAVED_DATA"];
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

如果有人在寻找swift版本:

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

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

Swift 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")
    }
    }
要存储和检索:

if let data = NSUserDefaults.standardUserDefaults().objectForKey("customArray") as? NSData
        {
             custom = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! [customData]
        }
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
        }

Swift 4引入了Codable协议,该协议为此类任务发挥了所有的魔力。只需使您的自定义结构/类符合它:

struct Player: Codable {
  let name: String
  let life: Double
}
要以默认值进行存储,您可以使用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)
对于数组和这类对象的其他容器类,它也会这样工作:

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


编辑了上面的我的文章以反映我的更改。@Chrisr您在NSUserDefaults=[NSUserDefaults standardUserDefaults]。。。应为NSUserDefaults*defaults.NSKeyedArchiver rocks。。。似乎它甚至会自动下降到NSArray或NSDictionary对象中,并对其中任何可编码的自定义对象进行编码。我不是学究,只是一个真正的问题,在init方法中使用合成setters ie self.property是否违反苹果的指导原则?@chrisr请更改
NSData*myencodeobject=[NSKeyedArchivedDataWithRootObject:object];
对于
NSData*encodedObject=[NSKeyedArchivedDataWithRootObject:object];
。变量不匹配。您是否有堆栈跟踪或有关崩溃的更多信息,例如导致崩溃的行号?我没有立即发现代码有任何错误,因此起点可能会有所帮助。在上面的示例中,您使用encodeObject存储self.life,它是一个int。您应该改用encodeInt。您的库假定您拥有要持久化的所有对象的属性,并且您想要持久化您拥有属性的所有对象。如果这是真的,您的库肯定会有所帮助,但我认为您会发现在许多情况下它并不是。仅持久化普通对象是有用的,我认为它的用法与Gson非常类似在Java中。你在看什么案例?这真的很有帮助,很高兴我一直在阅读答案;D关于我的自定义对象何时在其他自定义对象中具有属性?@Shial:如果你使其他自定义对象可架构,它将被保存下来,以提醒未来的人们,从Swift 3开始,“同步”您根本不应该调用它。@JozemiteApps为什么?或者您可以发布一个链接来解释这个主题吗?@code4latte我不知道它是否被更改了,但苹果的文档中说“synchronize()方法会定期自动调用,使内存缓存与用户的默认数据库保持同步。”以前,它说只有当您确定需要立即保存数据时才应该调用它。我已经读过几次了,用户不应该再调用它。只是提醒一下:
self
init
encode
中的变量之前不是强制性的。您能展示一下如何使用它初始化对象吗NSCoder@AbhishekThapliyal,我不明白你的意思初始化它就像一个charmNote,只有当所有实例成员本身都是可编码的时,才能免费获得
可编码的
行为——否则,您必须自己编写一些编码代码,或者干脆将这些成员类型也与
可编码的
一致。以此类推,递归地进行。只有在非常定制的情况下,您才能您需要编写编码代码。使用Swift 4最简单的方法。
try! PropertyListDecoder().decode([Player].self, from: storedArray)