如何使用 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 获取准确数据?