ios本地持久化存储

1.临时缓存

先说说临时缓存,临时缓存一般相当于用来管理应用程序中全局需要常用的一些内容。比如当前用户的ID或者当前的定位信息等。

常用的方式就是写一个静态变量,然后使用类方法调用。(或者单例类也行)

static NSMutableDictionary *_cacheDic;
复制代码
#pragma mark - 临时缓存(退出应用后消失)
+(id)cacheValueForKey:(SCCacheKey)key{
    return [[self cacheDic] valueForKey:[NSString stringWithFormat:@"%d",key]];
}

+(void)setCacheValue:(id)value forKey:(SCCacheKey)key{
    [[self cacheDic] setObject:value forKey:[NSString stringWithFormat:@"%d",key]];
}

+(void)deleteCacheForKey:(SCCacheKey)key{
    [[self cacheDic] removeObjectForKey:[NSString stringWithFormat:@"%d",key]];
}

2.本地存储NSUserdefaults,按KV存储,重启后还会存在。缺点1:只能存储NSString内容,缺点2:删除APP后会消失

复制代码
#pragma mark - 字段持久缓存(保存在数据库)
+(NSString *)storeValueForKey:(SCStoreKey)key{
    return [[NSUserDefaults standardUserDefaults] valueForKey:[@(key) stringValue]];
}

+(void)setStoreValue:(NSString *)value forKey:(SCStoreKey)key{
    [[NSUserDefaults standardUserDefaults] setObject:value forKey:[@(key) stringValue]];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

+(void)deleteStoreValueForKey:(SCStoreKey)key{
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:[@(key) stringValue]];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

3.NSKeyedArchiver(归档),可存储自定义对象。缺点:删除APP后会消失。

复制代码
#pragma mark - 对象持久缓存(保存在本地文件)
+(void)setStoreObject:(NSObject<NSCoding> *)obj forKey:(SCObjectKey)key{
    NSString *path=[SCSysconfig filePathByName:[NSString stringWithFormat:@"%d.domain",key]];
    [NSKeyedArchiver archiveRootObject:obj toFile:path];
}

+(NSObject<NSCoding> *)storeObjectForKey:(SCObjectKey)key{
    NSString *path=[SCSysconfig filePathByName:[NSString stringWithFormat:@"%d.domain",key]];
    NSObject<NSCoding> *obj=[NSKeyedUnarchiver unarchiveObjectWithFile:path];
    return obj;
}

+(void)deleteStoreObjectForKey:(SCObjectKey)key{
    NSString *path=[SCSysconfig filePathByName:[NSString stringWithFormat:@"%d.domain",key]];
    [SCFileOper removeFile:path];
}
复制代码

NSKeyedArchiver的使用方法可以参考这篇博客:http://www.cnblogs.com/xiaobaizhu/p/4011332.html


4.KeyChain的使用,删除APP后,重新安装还存在。(只要BoundleID一致)

Keychain即钥匙串,是用以在应用删除后还可以保存数据的一种方法。它的存储和应用的BoundleID有关。比如百度贴吧就实现了在应用删除后,再重新安装,如果Token还是有效的话,无需再登陆。

Keychain的使用官方接口略烦,一般采用第三方库,具体可以参考我的博客:http://www.cnblogs.com/rayshen/p/4671477.html

http://www.cnblogs.com/rayshen/p/4671477.html 内容

目前本地存储方式大致有:Sqlite,Coredata,NSUserdefaults。但他们都是在删除APP后就会被删除,如果长期使用存储,可以使用Keychain钥匙串来实现。

 

CHKeychain.h

复制代码
#import <Foundation/Foundation.h>

@interface CHKeychain : NSObject
+ (void)save:(NSString *)service data:(id)data;
+ (id)load:(NSString *)service;
+ (void)deleteData:(NSString *)service;
@end
复制代码

CHKeychain.m

复制代码
#import "CHKeychain.h"

@implementation CHKeychain

+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
    return [NSMutableDictionary dictionaryWithObjectsAndKeys:
            (__bridge id)kSecClassGenericPassword,(__bridge id)kSecClass,
            service, (__bridge id)kSecAttrService,
            service, (__bridge id)kSecAttrAccount,
            (__bridge id)kSecAttrAccessibleAfterFirstUnlock,(__bridge id)kSecAttrAccessible,
            nil];
}

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

+ (id)load:(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:(__bridge id)kSecReturnData];
    [keychainQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
    CFDataRef keyData = NULL;
    if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
        @try {
            ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
        } @catch (NSException *e) {
            NSLog(@"Unarchive of %@ failed: %@", service, e);
        } @finally {
        }
    }
    if (keyData)
        CFRelease(keyData);
    return ret;
}

+ (void)delete:(NSString *)service {
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
}
@end
复制代码

 

调用:(以生成UUID为例)

复制代码
#define UUIDKEY @"UUID"
#pragma mark--获取设备UUID
-(NSString*)uuid{
    if ([CHKeychain load:UUIDKEY]) {
        NSString *result = [CHKeychain load:UUIDKEY];
        _PhoneUUID=result;
        NSLog(@"已存在手机UUID:%@",result);
        return result;
    }
    else
    {
        CFUUIDRef puuid = CFUUIDCreate( nil );
        CFStringRef uuidString = CFUUIDCreateString( nil, puuid );
        NSString * result = (NSString *)CFBridgingRelease(CFStringCreateCopy( NULL, uuidString));
        CFRelease(puuid);
        CFRelease(uuidString);
        [CHKeychain save:UUIDKEY data:result];
        NSLog(@"初次创建手机UUID:%@",result);
        _PhoneUUID=result;
        return result;
    }
    return nil;
}
复制代码

 

以前貌似一定要通过自己新建Plist文件配置开发者账号来实现,现在好像已经有接口了,比较简单。

 

GITHUB:https://github.com/rayshen/ShenUUIDDemo


5 ios应用间数据共享

出处 http://www.cnblogs.com/rayshen/p/5072850.html

Demo先行:https://github.com/rayshen/GIKeychainGroupDemo

该demo里有2个工程,你先运行任何一个会存储一个值,再运行另一个会访问之前的app存储的值,并修改。

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

 

之前博客使用过Keychain,实现了数据删除APP后还能保存,但是并没有实现APP间的共享。

实现APP间的数据共享,主要依赖于在数据存入钥匙串时,使用同一个钥匙串条目。

主要分为两部分:

1.赋予应用对某个钥匙串条目的访问权限。

2.写入时配置钥匙串条目,对kSecAttrAccessGroup的值进行设置。

 

一、APP对钥匙串的访问权限:

(1)未对应用APP的entitlement(授权)进行配置时,APP使用钥匙串存储时,会默认存储在自身BundleID的条目下。

(2)对APP的entitlement(授权)进行配置后,说明APP有了对某个条目的访问权限。

 

钥匙串的可视化效果可参见Mac的APP-钥匙串访问。

 

APP钥匙串访问权限的配置方法:(这里XXXXX模拟器随意,但真机必须为自己开发者账号ID,否则无法通过编译)

1.新建一个Plist文件,在Plist中的数组中添加可以访问的条目的名字(如KeychainAccessGroups.plist),结构如下:

Plist代码:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>keychain-access-groups</key>
    <array>
        <string>XXXXX.GrassInfoAppFamily</string>
    </array>
</dict>
</plist>
复制代码

2.在Build-setting中进行配置,搜索entitlement,注意路径别配置错:

 

二、APP对钥匙串的操作:

钥匙串的操作接口都位于Security.framework框架下,它是一个sqlite数据库,位于/private/var/Keychains/keychain-2.db,其保存的所有数据都是加密过的。

其过程可以总结为:

1.配置查询字典,格式是NSMutableDictionary,需要配置的内容下次再分析,功能就相当于写一句SQL一样。

2.进行增(SecItemAdd)、删(SecItemDelete)、改(SecItemUpdate)、查(SecItemCopyMatching)。

代码Demo里面有,这里以增为例,下面有2个语句,一个是增加到自身BundleID的钥匙串条目,一个是增加到共享的条目中。

复制代码
//创建一个基本的查询字典
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
    return [NSMutableDictionary dictionaryWithObjectsAndKeys:
            (__bridge id)kSecClassGenericPassword,(__bridge id)kSecClass,
            service, (__bridge id)kSecAttrService,
            service, (__bridge id)kSecAttrAccount,
            (__bridge id)kSecAttrAccessibleAfterFirstUnlock,(__bridge id)kSecAttrAccessible,
            nil];
}

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

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

函数  [keychainQuery setObject:accessGroupItem forKey:(id)kSecAttrAccessGroup] 的配置,就是指定了这次写入时的钥匙串条目,不写入时默认为plist配置文件里第一个条目。

在查询中,也可以对查询的钥匙串条目进行配置,默认会对所有有权限的条目进行搜索

 

三、keychain的组成:

参考博客:http://my.oschina.net/w11h22j33/blog/206713

每一个keyChain的组成如图,整体是一个字典结构.
1.kSecClass key 定义属于那一种类型的keyChain
2.不同的类型包含不同的Attributes,这些attributes定义了这个item的具体信息
3.每个item可以包含一个密码项来存储对应的密码

 

对于最常用密码类型,我们应该如下配置

[wrapper setObject:kSecClassGenericPassword forKey:(id)kSecClass];//class

[wrapper setObject:@"username" forKey:(id)kSecAttrAccount];//key

[wrapper setObject:@"password"forKey:(id)kSecValueData];//value

[wrapper setObject:(id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(id)kSecAttrAccessible];

kSecAttrAccessiblein变量用来指定这个应用合适需要访问这个数据。我们需要对这个选项特别注意,并且使用最严格的选项。这个键(key)可以设置6种值。

你可以参考以下:

 

四、安全

前面说到,APP能够访问的keychain数据是通过其entitlements文件指定的。

但是!!如果使用带有一个*通配符的entitlments,因此它能够访问keychain中的所有条目。。或者,如果用一个包含所有访问组(access group)的entitlements文件,也能够访问所有的keychain数据。

但以上仅限模拟器,在真机调试时,假如你plist里的条目,和自己调试文件(pro file)的ID不一致时(比如:EC0880A1.company),进行真机编译的时候是会报错的。

所以真正要保证全家桶,还需要保证这些APP在同一个开发者ID下才行。 

 

前面关于在未配置keychain-access-group的情况下,我参考别人说是默认存储在该BundleID的条目下,但实测并非存储在该条目下。具体为什么我也暂时不清楚。但是可以肯定的是,在iOS的沙盒机制中,你默认存储的条目下的K-V,其他APP是无法访问的(除了模拟器中的通配符*,真机是不行的)。

 

关于Keychain还有很多值得挖掘的,比如具体在sqlite数据库的表单存储方式等,有错欢迎指正。


更多内容可以搜索对增删改查的封装类KeychainItemWrapper的使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值