iOS—keychain,NSUserDefaults


iOS本地数据存储方面,常用的方法有好几种,比如plist,xml,NSUserDefaults,keychain等,但是,各自都有
各自的特点,通常情况下,我们用NSUserDefaults存储数据信息,但是对于一些私密信息,比如密码、证书等等,就需要使用更为安全的keychain了。keychain里保存的信息不会因App被删除而丢失,在用户重新安装App后依然有效,数据还在。

使用苹果官方发布的KeychainItemWrapper或者SFHFKeychainUtils很方便

APPLE: https://developer.apple.com/library/ios/samplecode/GenericKeychain/Introduction/Intro.html

1、NSUserDefaults

  一般情况下,由于NSUserDefaults存储的数据不够安全,用户可以随意根据自己的需要更改里面的数据,所以NSUserDefaults一般只会被用来存储App的设置数据等,这就好比web中的cookie,同样可以存储数据,但是不够安全。

  上代码

  Setting.h:

1 #import <Foundation/Foundation.h> 
2  
3 typedef  enum { 
4     ST_Music = 100, 
5     ST_Volume, 
6     ST_Push, 
7     ST_Num 
8 }SettingType;//各种设置的枚举 
9 
10 @interface Setting : NSObject
11 //读取getter
12 + (bool)getIsEnableByType:(SettingType)type;
13 //设置setter
14 + (void)setIsEnable:(bool)isEnable byType:(SettingType)type;
15 //清空数据
16 + (void)resetData;
17 
18 @end

  setting.mm

1 #import "Setting.h" 
2  
3 #define KEY_SETTING       @"BeautyPics_" 
4  
5 @implementation Setting 
6  
7 + (void)resetData 
8 { 
9     for (SettingType type = ST_Music; type < ST_Num; type++) {
10         [self setIsEnable:true byType: type];//默认全部为true
11     }
12 }
13 
14 + (bool)getIsEnableByType:(SettingType)type
15 {
16     NSString * key = [KEY_SETTING stringByAppendingFormat:@"%d", type];
17     id isEnable = [[NSUserDefaults standardUserDefaults] objectForKey: key];
18     if (isEnable == nil) {
19         [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool: true] forKey:key];
20         [[NSUserDefaults standardUserDefaults] synchronize];
21         return true;
22     }
23     return [isEnable boolValue];
24 }
25 
26 + (void)setIsEnable:(bool)isEnable byType:(SettingType)type
27 {
28     NSString * key = [KEY_SETTING stringByAppendingFormat:@"%d", type];
29  
30     if ([self getIsEnableByType: type] != isEnable) {
31         [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool: isEnable] forKey:key];
32         [[NSUserDefaults standardUserDefaults]synchronize];
33     }
34 }
35 
36 @end

  以上代码一目了然,需要注意的是,每次设置完数据之后,一定要通过[[NSUserDefaults standardUserDefaults] synchronize]来同步,否则数据将不会保存起来。

 

2、keychain

  既然NSUserDefaults不够安全,那么有没有一些比较安全的本地数据存储方式呢,答案当然是有的,这就是keychain,keychain只能该App本身访问,其他的App不能访问(当然现在只要通过设置是可以的。但是要通过这种形式:App1同意App2访问,App2才能访问到App1所存储的keychain)。关于keychain的文章很多,我不重复其原理,这里举出其用法,keychain的存储方式同样也是字典,也就是NSMutableDictionary,下面上代码:

我们可以把KeyChain理解为一个Dictionary,所有数据都以key-value的形式存储,可以对这个Dictionary进行add、update、get、delete这四个操作。对于每一个应用来说,KeyChain都有两个访问区,私有区和公共区。私有区是一个sandbox,本程序存储的任何数据都对其他程序不可见。而要想在将存储的内容放在公共区,需要先声明公共区的名称,官方文档管这个名称叫“keychain access group”,声明的方法是新建一个plist文件,名字随便起,内容如下:
 
 
“yourAppID.com.yourCompany.whatever”就是你要起的公共区名称,除了whatever字段可以随便定之外,其他的都必须如实填写。这个文件的路径要配置在 Project->build setting->Code Signing Entitlements里,否则公共区无效,配置好后,须用你正式的证书签名编译才可通过,否则xcode会弹框告诉你code signing有问题。所以,苹果限制了你只能同公司的产品共享KeyChain数据,别的公司访问不了你公司产品的KeyChain。

  (1)保存数据。其中data可以是NSArray、NSSet,NSDictionary等,一般情况下这三种数据结构已经足够用了。service就是要存储的key了。

 

+ (void)saveData:(NSString *)service data:(id)data{    
//Get search dictionary    
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];    
//Delete old item before add new item    
SecItemDelete((CFDictionaryRef)keychainQuery);    
//Add new object to search dictionary(Attention:the data format)    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];    
//Add item to keychain with the search dictionary    
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
}

 

  (2)读取数据。直接通过保存数据是使用的key即可读取所存数据。

+ (id)loadData:(NSString *)service{    
id ret = nil;    
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];    
//Configure the search setting    
//Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue    
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];    
[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];    
CFDataRef keyData = NULL;    
if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
        @try {
            ret = [NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)keyData];
        } @catch (NSException *e) {
            NSLog(@"Unarchive of %@ failed: %@", service, e);
        } @finally {        }
    }
    if (keyData)
        CFRelease(keyData);
    return ret;
}
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service{
    NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:(id)kSecClassGenericPassword,(id)kSecClass,service, (id)kSecAttrService,service, (id)kSecAttrAccount,(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,nil];
    return dict;
}

  

  (2)demo。以上已经可以实现使用keychain读取和存储数据了。那么接下来我们来一小段demo。

比如我要存储NSDictionary,用户名为“阿泰”,密码为“123456”,则:

1 NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:@"阿泰",@"key_username",@"123456",@"key_pwd", nil];2  [Keychain saveData:KEY_USER_DATA_ALL data: dict];

  其中 KEY_USER_DATA_ALL 为所保存的数据的key,可以通过这个key把保存的字典读出来。

  读取时,如下:

1 NSDictionary * loadDict = [Keychain loadData: KEY_USER_DATA_ALL];

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值