关于ios:如何在NSUserDefaults中存储自定义对象

How to store custom objects in NSUserDefaults

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

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

这是我在NSUserDefaults中放入自定义类"player"时得到的错误消息。现在,我已经读到,显然NSUserDefaults只存储一些类型的信息。那么,我如何将我的对象放入NSUserDefaults中呢?

我读到应该有一种方法来"编码"我的自定义对象,然后把它放进去,但我不确定如何实现它,将感谢您的帮助!谢谢您!

***编辑***

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

ZZU1

代码从:拯救阶级


我创建了一个图书馆RMMAPPER(HTTPS://github.com/roomorama/RMMAPER)以帮助将用户对象保存到Nuserdefaults easier and more convenient,因为执行编码和initwithcoder是超级博灵!

标记一个可存档的类别,只需使用:#import"NSObject+RMArchivable.h"

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 NSUserDefaultsto save and retrieve custom objects:

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

希望这能帮助你。谢谢