如何使用 NSUserDefaults 在 iOS 中保存非属性值?

Posted

技术标签:

【中文标题】如何使用 NSUserDefaults 在 iOS 中保存非属性值?【英文标题】:How to save non property values in iOS using NSUserDefaults? 【发布时间】:2013-08-06 13:33:22 【问题描述】:

我正在保存从一个社交网络网站获得的AccessToken。当我保存它时,我知道我们无法直接保存 ios SDK 中的非属性值。

然后从教程中我知道我应该实现NSCoding 类。然后我就这样做了。

 NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:self.accessToken];
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:myEncodedObject forKey:@"myEncodedObjectKey"];
    [defaults synchronize];

   NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   NSData *myEncodedObject = [defaults objectForKey:@"myEncodedObjectKey"];
            LOAToken *obj = (LOAToken *)[NSKeyedUnarchiver unarchiveObjectWithData: myEncodedObject];

我实现了NSCoding 委托,但我不知道如何实现delegate methods。然后当我运行这段代码时,我得到了错误

`"-[LOAToken encodeWithCoder:]: unrecognized selector sent to instance 0xa2bb970"

我无法将NSCoding 与我的代码一起实现。有什么建议么?还有没有其他方法可以存储AccessToken等非属性值以供进一步使用。

编辑:

我正在获取此 LinkedIn 的访问令牌并希望像这样存储:

self.accessToken = [[LOAToken alloc] initWithHTTPResponseBody:responseBody];


        // The accessToken Printed....Here I have the AccessToken Value.
        NSLog(@"Access===%@",self.accessToken);

        NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:self.accessToken];
        NSLog(@"myEncodedObject===%@",myEncodedObject);
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        [defaults setObject:myEncodedObject forKey:@"myEncodedObjectKey"];
        [defaults synchronize];

这已经崩溃,因为我没有使用 NSCoding 实现,我在你建议的 LOAToken 类中使用。

我在LAToken类中做了一个变量值,实现了这两种方法。

-(void)encodeWithCoder:(NSCoder *)encoder

    [encoder encodeObject:value forKey:@"Value"];


-(id)initWithCoder:(NSCoder *)decoder

    self.value = [decoder decodeObjectForKey:@"Value"];
    return self;

然后在检索时我正在使用它。

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSData *myEncodedObject = [defaults objectForKey:@"myEncodedObjectKey"];
    LOAToken *obj = (OAToken *)[NSKeyedUnarchiver unarchiveObjectWithData: myEncodedObject];

当我在归档和取消归档时都打印数据时,打印的数据是相同的。这可能是由于两个原因。

    我解压的数据是对的,取回的方式是错的,你可以通过我上面的代码提示一下。

    我存档时保存的数据为空,所以保存了空。

保存访问令牌是否有任何限制。这可能是空输出的原因。

我得到的输出是这样的:

oauth_token "(null)" oauth_token_secret "(null)" oauth_verifier "(null)"

文本 "oauth_token" "oauth_token_secret" "oauth_verifier" 来自 AccessToken 但它们的值为空。

编辑 2:这是 LinkedIn 的 OAToken 类,我在其中获取访问令牌。我在编码方法中传递的令牌相同

OAToken.h

#import <Foundation/Foundation.h>

@interface OAToken : NSObject 
@protected
    NSString *key;
    NSString *secret;
    NSString *session;
    NSString *verifier;
    NSNumber *duration;
    NSMutableDictionary *attributes;
    NSDate *created;
    BOOL renewable;
    BOOL forRenewal;

    OAToken *value;

@property(retain, readwrite) NSString *key;
@property(retain, readwrite) NSString *secret;
@property(retain, readwrite) NSString *session;
@property(retain, readwrite) NSString *verifier;
@property(retain, readwrite) NSNumber *duration;
@property(retain, readwrite) NSMutableDictionary *attributes;
@property(readwrite, getter=isForRenewal) BOOL forRenewal;
@property (nonatomic,retain) OAToken *value;

- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret;
- (id)initWithKey:(NSString *)aKey 
           secret:(NSString *)aSecret 
          session:(NSString *)aSession
         verifier:(NSString *)aVerifier
         duration:(NSNumber *)aDuration 
       attributes:(NSMutableDictionary *)theAttributes 
          created:(NSDate *)creation
        renewable:(BOOL)renew;

- (id)initWithHTTPResponseBody:(NSString *)body;

- (id)initWithUserDefaultsUsingServiceProviderName:(NSString *)provider prefix:(NSString *)prefix;
- (int)storeInUserDefaultsWithServiceProviderName:(NSString *)provider prefix:(NSString *)prefix;

- (BOOL)isValid;

- (void)setAttribute:(NSString *)aKey value:(NSString *)aValue;
- (NSString *)attribute:(NSString *)aKey;
- (void)setAttributesWithString:(NSString *)aAttributes;
- (NSString *)attributeString;

- (BOOL)hasExpired;
- (BOOL)isRenewable;
- (void)setDurationWithString:(NSString *)aDuration;
- (void)setVerifierWithUrl:(NSURL *)aURL;
- (BOOL)hasAttributes;
- (NSMutableDictionary *)parameters;

- (BOOL)isEqualToToken:(OAToken *)aToken;

+ (void)removeFromUserDefaultsWithServiceProviderName:(const NSString *)provider prefix:(const NSString *)prefix;

@end

OAToken.m

#import "NSString+URLEncoding.h"
#import "OAToken.h"

@interface OAToken (Private)

+ (NSString *)settingsKey:(const NSString *)name provider:(const NSString *)provider prefix:(const NSString *)prefix;
+ (id)loadSetting:(const NSString *)name provider:(const NSString *)provider prefix:(const NSString *)prefix;
+ (void)saveSetting:(NSString *)name object:(id)object provider:(const NSString *)provider prefix:(const NSString *)prefix;
+ (NSNumber *)durationWithString:(NSString *)aDuration;
+ (NSMutableDictionary *)attributesWithString:(NSString *)theAttributes;

@end

@implementation OAToken

@synthesize key, secret, session, verifier, duration, attributes, forRenewal;
@synthesize value;

#pragma mark Encode

-(void)encodeWithCoder:(NSCoder *)encoder

    // This prints the value....
    NSLog(@"value===%@",self.value);
    [encoder encodeObject:self.value forKey:@"Value"];


-(id)initWithCoder:(NSCoder *)decoder

    OAToken *hell= [decoder decodeObjectForKey:@"Value"];
    // This don't have the value.It is null.
    NSLog(@"hell===%@",hell);

    return self;


#pragma mark init

- (id)init 
    return [self initWithKey:nil secret:nil];


- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret 
    return [self initWithKey:aKey secret:aSecret session:nil verifier:nil duration:nil
                  attributes:nil created:nil renewable:NO];




- (id)initWithKey:(NSString *)aKey 
           secret:(NSString *)aSecret 
          session:(NSString *)aSession
         verifier:(NSString *)aVerifier
         duration:(NSNumber *)aDuration 
       attributes:(NSMutableDictionary *)theAttributes 
          created:(NSDate *)creation
        renewable:(BOOL)renew 

    [super init];
    self.key = aKey;
    self.secret = aSecret;
    self.session = aSession;
    self.verifier = aVerifier;
    self.duration = aDuration;
    self.attributes = theAttributes;
    created = [creation retain];
    renewable = renew;
    forRenewal = NO;

    return self;


- (void)setVerifierWithUrl:(NSURL *)aURL

    NSString *query = [aURL query];
    NSArray *pairs = [query componentsSeparatedByString:@"&"];

    for (NSString *pair in pairs) 
    
        NSArray *elements = [pair componentsSeparatedByString:@"="];
        if ([[elements objectAtIndex:0] isEqualToString:@"oauth_verifier"]) 
        
            self.verifier = [elements objectAtIndex:1];
         
    


- (id)initWithHTTPResponseBody:(const NSString *)body 

    NSString *aKey = nil;
    NSString *aSecret = nil;
    NSString *aSession = nil;
    NSString *aVerifier = nil;
    NSNumber *aDuration = nil;
    NSDate *creationDate = nil;
    NSMutableDictionary *attrs = nil;
    BOOL renew = NO;
    NSArray *pairs = [body componentsSeparatedByString:@"&"];

    for (NSString *pair in pairs) 
    
        NSArray *elements = [pair componentsSeparatedByString:@"="];
        if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token"]) 
        
            aKey = [elements objectAtIndex:1];
         
        else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token_secret"]) 
        
            aSecret = [elements objectAtIndex:1];
         
        else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_verifier"]) 
        
            aVerifier = [elements objectAtIndex:1];
         
        else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_session_handle"]) 
        
            aSession = [elements objectAtIndex:1];
         
        else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token_duration"]) 
        
            aDuration = [[self class] durationWithString:[elements objectAtIndex:1]];
            creationDate = [NSDate date];
         
        else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token_attributes"]) 
        
            attrs = [[self class] attributesWithString:[[elements objectAtIndex:1] decodedURLString]];
         
        else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token_renewable"]) 
        
            NSString *lowerCase = [[elements objectAtIndex:1] lowercaseString];
            if ([lowerCase isEqualToString:@"true"] || [lowerCase isEqualToString:@"t"]) 
                renew = YES;
            
        
    

    value=[self initWithKey:aKey
                                secret:aSecret
                               session:aSession
                              verifier:aVerifier
                              duration:aDuration
                            attributes:attrs
                               created:creationDate
                             renewable:renew];

    return [self initWithKey:aKey 
                      secret:aSecret 
                     session:aSession 
                    verifier:aVerifier
                    duration:aDuration
                  attributes:attrs 
                     created:creationDate 
                   renewable:renew];


- (id)initWithUserDefaultsUsingServiceProviderName:(const NSString *)provider prefix:(const NSString *)prefix 
    [super init];
    self.key = [OAToken loadSetting:@"key" provider:provider prefix:prefix];
    self.secret = [OAToken loadSetting:@"secret" provider:provider prefix:prefix];
    self.session = [OAToken loadSetting:@"session" provider:provider prefix:prefix];
    self.verifier = [OAToken loadSetting:@"verifier" provider:provider prefix:prefix];
    self.duration = [OAToken loadSetting:@"duration" provider:provider prefix:prefix];
    self.attributes = [OAToken loadSetting:@"attributes" provider:provider prefix:prefix];
    created = [OAToken loadSetting:@"created" provider:provider prefix:prefix];
    renewable = [[OAToken loadSetting:@"renewable" provider:provider prefix:prefix] boolValue];

    if (![self isValid]) 
        [self autorelease];
        return nil;
    

    return self;


#pragma mark dealloc

- (void)dealloc 
    self.key = nil;
    self.secret = nil;
    self.duration = nil;
    self.attributes = nil;
    [super dealloc];


#pragma mark settings

- (BOOL)isValid 
    return (key != nil && ![key isEqualToString:@""] && secret != nil && ![secret isEqualToString:@""]);


- (int)storeInUserDefaultsWithServiceProviderName:(const NSString *)provider prefix:(const NSString *)prefix 
    [OAToken saveSetting:@"key" object:key provider:provider prefix:prefix];
    [OAToken saveSetting:@"secret" object:secret provider:provider prefix:prefix];
    [OAToken saveSetting:@"created" object:created provider:provider prefix:prefix];
    [OAToken saveSetting:@"duration" object:duration provider:provider prefix:prefix];
    [OAToken saveSetting:@"session" object:session provider:provider prefix:prefix];
    [OAToken saveSetting:@"verifier" object:verifier provider:provider prefix:prefix];
    [OAToken saveSetting:@"attributes" object:attributes provider:provider prefix:prefix];
    [OAToken saveSetting:@"renewable" object:renewable ? @"t" : @"f" provider:provider prefix:prefix];

    [[NSUserDefaults standardUserDefaults] synchronize];
    return(0);


#pragma mark duration

- (void)setDurationWithString:(NSString *)aDuration 
    self.duration = [[self class] durationWithString:aDuration];


- (BOOL)hasExpired

    return created && [created timeIntervalSinceNow] > [duration intValue];


- (BOOL)isRenewable

    return session && renewable && created && [created timeIntervalSinceNow] < (2 * [duration intValue]);



#pragma mark attributes

- (void)setAttribute:(const NSString *)aKey value:(const NSString *)aAttribute 
    if (!attributes) 
        attributes = [[NSMutableDictionary alloc] init];
    
    [attributes setObject: aAttribute forKey: aKey];


- (void)setAttributes:(NSMutableDictionary *)theAttributes 
    [attributes release];
    if (theAttributes) 
        attributes = [[NSMutableDictionary alloc] initWithDictionary:theAttributes];
    else 
        attributes = nil;
    



- (BOOL)hasAttributes 
    return (attributes && [attributes count] > 0);


- (NSString *)attributeString 
    if (![self hasAttributes]) 
        return @"";
    

    NSMutableArray *chunks = [[NSMutableArray alloc] init];
    for(NSString *aKey in self->attributes) 
        [chunks addObject:[NSString stringWithFormat:@"%@:%@", aKey, [attributes objectForKey:aKey]]];
    
    NSString *attrs = [chunks componentsJoinedByString:@";"];
    [chunks release];
    return attrs;


- (NSString *)attribute:(NSString *)aKey

    return [attributes objectForKey:aKey];


- (void)setAttributesWithString:(NSString *)theAttributes

    self.attributes = [[self class] attributesWithString:theAttributes];


- (NSMutableDictionary *)parameters

    NSMutableDictionary *params = [[[NSMutableDictionary alloc] init] autorelease];

    if (key) 
    
        [params setObject:key forKey:@"oauth_token"];
        if ([self isForRenewal]) 
        
            [params setObject:session forKey:@"oauth_session_handle"];
        
     
    else 
    
        if (duration) 
        
            [params setObject:[duration stringValue] forKey: @"oauth_token_duration"];
        
        if ([attributes count]) 
        
            [params setObject:[self attributeString] forKey:@"oauth_token_attributes"];
        
    

    if (verifier)
    
        [params setObject:verifier forKey:@"oauth_verifier"];
    
    return params;


#pragma mark comparisions

- (BOOL)isEqual:(id)object 
    if([object isKindOfClass:[self class]]) 
        return [self isEqualToToken:(OAToken *)object];
    
    return NO;


- (BOOL)isEqualToToken:(OAToken *)aToken 
    /* Since ScalableOAuth determines that the token may be
     renewed using the same key and secret, we must also
     check the creation date */
    if ([self.key isEqualToString:aToken.key] &&
        [self.secret isEqualToString:aToken.secret]) 
        /* May be nil */
        if (created == aToken->created || [created isEqualToDate:aToken->created]) 
            return YES;
        
    

    return NO;


#pragma mark class_functions

+ (NSString *)settingsKey:(NSString *)name provider:(NSString *)provider prefix:(NSString *)prefix 
    return [NSString stringWithFormat:@"OAUTH_%@_%@_%@", provider, prefix, [name uppercaseString]];


+ (id)loadSetting:(NSString *)name provider:(NSString *)provider prefix:(NSString *)prefix 
    return [[NSUserDefaults standardUserDefaults] objectForKey:[self settingsKey:name
                                                                        provider:provider
                                                                          prefix:prefix]];


+ (void)saveSetting:(NSString *)name object:(id)object provider:(NSString *)provider prefix:(NSString *)prefix 
    [[NSUserDefaults standardUserDefaults] setObject:object forKey:[self settingsKey:name
                                                                            provider:provider
                                                                              prefix:prefix]];


+ (void)removeFromUserDefaultsWithServiceProviderName:(NSString *)provider prefix:(NSString *)prefix 
    NSArray *keys = [NSArray arrayWithObjects:@"key", @"secret", @"created", @"duration", @"session", @"verifier", @"attributes", @"renewable", nil];
    for(NSString *name in keys) 
        [[NSUserDefaults standardUserDefaults] removeObjectForKey:[OAToken settingsKey:name provider:provider prefix:prefix]];
    


+ (NSNumber *)durationWithString:(NSString *)aDuration 
    NSUInteger length = [aDuration length];
    unichar c = toupper([aDuration characterAtIndex:length - 1]);
    int mult;
    if (c >= '0' && c <= '9') 
        return [NSNumber numberWithInt:[aDuration intValue]];
    
    if (c == 'S') 
        mult = 1;
     else if (c == 'H') 
        mult = 60 * 60;
     else if (c == 'D') 
        mult = 60 * 60 * 24;
     else if (c == 'W') 
        mult = 60 * 60 * 24 * 7;
     else if (c == 'M') 
        mult = 60 * 60 * 24 * 30;
     else if (c == 'Y') 
        mult = 60 * 60 * 365;
     else 
        mult = 1;
    

    return [NSNumber numberWithInt: mult * [[aDuration substringToIndex:length - 1] intValue]];


+ (NSMutableDictionary *)attributesWithString:(NSString *)theAttributes 
    NSArray *attrs = [theAttributes componentsSeparatedByString:@";"];
    NSMutableDictionary *dct = [[NSMutableDictionary alloc] init];
    for (NSString *pair in attrs) 
        NSArray *elements = [pair componentsSeparatedByString:@":"];
        [dct setObject:[elements objectAtIndex:1] forKey:[elements objectAtIndex:0]];
    
    return [dct autorelease];


#pragma mark description

- (NSString *)description 
    return [NSString stringWithFormat:@"oauth_token \"%@\" oauth_token_secret \"%@\" oauth_verifier \"%@\"", key, secret, verifier];


@end

【问题讨论】:

你的类必须支持 NSCoding 协议。那么只有你可以使用 NSKeyedArchiver 【参考方案1】:

您不需要任何委托方法。相反,LOAToken 应该实现 initWithCoder:encodeWithCoder: 方法来实际保存和恢复它包含的信息。这就是实现NSCoding协议的意义。

您可能想阅读归档文档here。


一旦您最初从 Web 响应或类似响应创建了令牌,您应该使用 storeInUserDefaultsWithServiceProviderName: 将其存储到用户默认值中。然后,当您想再次使用它时,请致电initWithUserDefaultsUsingServiceProviderName:。你不需要自己做任何编码和解码。

【讨论】:

感谢您的回复。我已经摆脱了错误,但结果中缺少令牌。我得到的是:oauth_token "(null)" oauth_token_secret "(null)" oauth_verifier " (null)"。任何线索我错在哪里? 您可能想创建一个新问题来显示您的编码方法。 我已经用解释更新了我的问题。请给我一个出路。 如何保存value应该包含3条token信息? Self.accessToken 是具有所有三个值的 LOAToken 类型。我尝试将“value”设置为 LOAToken 类型,然后它返回的结果与我尝试将“value”作为字符串类型相同,即 =oauth_token ” (null)" oauth_token_secret "(null)" oauth_verifier "(null)"【参考方案2】:

取消归档数据

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:@"key"];
NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];

存档数据

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];       
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
[defaults setValue:data forKey:@"key"];
[defaults synchronize];

【讨论】:

您无法保存自定义类,如 uiview 等。但您可以使用该类的包装类保存该值。

以上是关于如何使用 NSUserDefaults 在 iOS 中保存非属性值?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NSUserDefaults 存储和加载 BOOL 数组或 int 数组? (IOS 7)

如何使用 Xcode 9.3 在 iOS 开发设备中检查 [NSUserDefaults standardUserDefaults] [重复]

如何从 Objective C、iOS 中的 NSUserDefaults 获取准确数据?

如何从 Xamarin.iOS 中的 iOS 扩展调用 NSUserDefaults Observer

为啥在 iOS 中使用 NSUserDefaults?

iOS NSUserDefaults synchronize存储遇到的坑