一.沙盒
每个iOS应用都有⾃己的应⽤沙盒(应用沙盒就是文件系统目录), 与其他文件系统隔离。应⽤必须待在⾃己的沙盒里,其他应用不能访问该沙盒(在iOS8中已经开放访问)
应⽤沙盒的文件系统⽬录,如下图所示可以看出, 沙盒里面有四个文件夹, 分别是Documents, Lirary, SystemData, tmp
Lirary里面又有Caches, Preferences两个文件夹
1. Documents
默认是备份的(使用itunes或者iCloud会备份的时候)
大文件尽量不要存储在这个目录下(视频文件)如果不做任何处理,审核,检查出来,就会被拒
如果不想被拒, 下面的操作二选其一
① 文件非备份操作设置
因为Documents文件夹默认是备份的, 我们需要写以下代码
// 设置非备份
//#import
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL*)URL
{
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
}
这样documents文件夹下的东西默认就不会备份了
② 大文件放到其他目录
2. Lirary
Lirary文件下有两个文件夹Caches和Preferences, 都是默认备份的
Caches 缓存(比如系统截屏,网络缓存等 )
Preferences(plist文件 NSUserDefaults)
① Caches
当我们使用截屏, 或者网络请求的时候, 会在Caches产生缓存.
网络请求示例代码:
//网络缓存
- (void)netLoadTask{
NSString *urlStr = [NSString stringWithFormat:@"http://svr.tuliu.com/center/front/app/util/updateVersions?versions_id=1&system_type=1"];
NSURLSession *session = [NSURLSession sharedSession];
// 默认的缓存存在disk, 这时候Caches里面有缓存
// [NSURLSessionConfiguration defaultSessionConfiguration];
// 缓存存在内存, 这时候在Caches里面是没有缓存的
// NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
NSURLSessionTask *task = [session dataTaskWithURL:[NSURL URLWithString:urlStr] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSDictionary *infoDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@", infoDict);
}];
[task resume];
}
运行代码, 验证如下:
其中, 网络请求的缓存是一些非加密的数据库文件
使用:比如我们的app有一个清除缓存的功能,这时候我们首先要清除Caches文件夹的内容, 其次再清除我们自己创建的一些文件夹
② Preferences
我们使用NSUserDefaults存储的数据在这里
3. SystemData
4. tmp
tmp (临时文件夹)
当内存不足的时候系统有可能会把tmp清空, 所以一些重要的文件不会要放这里
5. Bundle
Bundle是资源包的意思, 就是个文件夹, 一些HUD或者MJRefresh都带有Bundle, 不参与编译, 里面别放代码
如下图, 项目中又添加了一个Bundle如果想要获取主Bundle的图片直接
_imageView.image = [UIImage imageNamed:@"2.png"];
但是如果想要获取新添加的Bundle的图片, 就要先拿到新添加的Bundle, 如下
// 用代码取路径
NSString *boudlePath = [[NSBundle mainBundle] pathForResource:@"EOCBundle" ofType:@"bundle"];
//获取新的Bundle
NSBundle *eocBoundle = [NSBundle bundleWithPath:boudlePath];
NSString *imagePath = [eocBoundle pathForResource:@"11" ofType:@"png"];
_imageView.image = [UIImage imageWithContentsOfFile:imagePath];
获取主Bundle资源文件路径的方法:
//例如获取程序包中一个图片资源(apple.png)路径的方法:
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@”apple” ofType:@”png”];
UIImage *appleImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
//代码中的mainBundle类方法用于返回一个代表应用程序包的对象。
补充:项目中黄色文件夹和蓝色文件夹的区别
我们项目中一般创建的都是黄色文件夹, 黄色的是参与编译的
获取APP沙盒路径的两种方式
- 使用NSSearchPathForDirectoriesInDomains方法
//Domain领域 Mask面具,掩护,掩饰
//1. 获取Documents目录路径的方法:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString *docDir = [paths objectAtIndex:0];
//2. 获取Caches目录路径的方法:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask,YES);
NSString *cachesDir = [paths objectAtIndex:0];
/*
关于参数:
NSUserDomainMask 在用户目录下查找
YES 代表用户目录的~符号在iOS中识别~
NSDocumentDirectory 查找Documents文件夹
*/
- 使用沙盒根目录拼接
利用沙盒根目录拼接”Documents”字符串
不建议采用,因为新版本的操作系统可能会修改目录名
NSString *home = NSHomeDirectory();
NSString *documents = [home stringByAppendingPathComponent:@"Documents"];
另外可以直接使用NSTemporaryDirectory()直接获取tmp的路径
二. NSUserDefaults
很多iOS应用都支持偏好设置,比如保存用户名、密码、字体大小等设置,iOS提供了一套标准的解决方案来为应用加入偏好设置功能
每个应用都有个NSUserDefaults实例,通过它来存取偏好设置。比如,保存用户名、字体大小、是否自动登录
它是XML属性列表, 属性列表是一种XML格式的文件,拓展名为plist
存储位置:Library -> Preferences 存储格式:简单使用:
//1.获取NSUserDefaults对象
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
//2保存数据
[defaults setObject:@"yangyong" forKey:@"name"];
//3.强制让数据立刻保存
[defaults synchronize];
//4.读取数据
NSString *name=[defaults objectForKey:@"name"];
注意:
使用偏好设置对数据进行保存之后, 它保存到系统的时间是不确定的,会在将来某一时间点自动将数据保存到Preferences文件夹下面,如果需要即刻将数据存储,可以使用[defaults synchronize]
所有的信息都写在一个plist文件中. 根据plist文件可以保存的数据类型我们也可以推测出NSUserDefaults可以保存的数据类型, 所以有些类型我们要转成NSData再保存在NSUserDefaults中(不推荐)
关于沙盒可参考:沙盒和NSBundle
三. NSKeyedUnarchiver(归档)
在使用plist进行数据存储和读取,只适用于系统自带的一些常用类型才能用,且必须先获取路径相对麻烦
对于NSUserDefaults, 主要用于存储应用的设置信息, 而且它的本质还是plist文件, plist都能直接打开, 相对不安全
这两者都有一个致命的缺陷,只能存储常用的类型(plist文件支持的类型)。所以我们要介绍归档
归档可以实现把自定义的对象存放在文件中
使用方法:
先自定义一个对象, 对象遵守NSCoding协议,并且实现两个协议方法, 如果不实现两个协议方法, 会报错
@interface Person : NSObject
//姓名
@property(nonatomic,copy)NSString *name;
//年龄
@property(nonatomic,assign)int age;
//身高
@property(nonatomic,assign)double height;
@end
// 当将一个自定义对象保存到文件的时候就会调用该方法
// 在该方法中说清楚存储自定义对象的哪些属性
-(void)encodeWithCoder:(NSCoder *)aCoder
{
NSLog(@"调用了encodeWithCoder:方法");
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
[aCoder encodeDouble:self.height forKey:@"height"];
}
// 当从文件中读取一个对象的时候就会调用该方法
// 在该方法中说清楚怎么读取文件中的对象
-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
NSLog(@"调用了initWithCoder:方法");
//注意:在构造方法中需要先初始化父类的方法
if (self=[super init]) {
self.name=[aDecoder decodeObjectForKey:@"name"];
self.age=[aDecoder decodeIntegerForKey:@"age"];
self.height=[aDecoder decodeDoubleForKey:@"height"];
}
return self;
}
控制器代码如下:
//创建对象
Person *p = [[Person alloc] init];
p.name = @"徐金城";
p.age = 15;
p.height = 180;
//获取路径
//NSSearchPathForDirectoriesInDomains返回的是一个数组, 内容只有一个
NSString *docPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
NSString *path=[docPath stringByAppendingPathComponent:@"xujincheng"];
NSLog(@"path=%@",path);
//将自定义的对象保存到文件中
//[NSKeyedArchiver archivedDataWithRootObject:<#(nonnull id)#> requiringSecureCoding:<#(BOOL)#> error:<#(NSError * _Nullable __autoreleasing * _Nullable)#>];
BOOL flag = [NSKeyedArchiver archiveRootObject:p toFile:path];//YES归档成功,NO归档失败
//读取对象
Person *p2 = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSLog(@"%@,%d,%.1f",p2.name,p2.age,p2.height);
运行后,归档的文件如下:
打印结果如下:
path=/Users/xujincheng/Library/Developer/CoreSimulator/Devices/A3BD602E-4365-4B04-9FA4-61186DCE8ADA/data/Containers/Data/Application/B4343BA9-74F9-44D9-923D-5318F2086CDA/Documents/xujincheng
2019-11-05 10:20:00.535875+0800 归档[82834:6785993] 调用了encodeWithCoder:方法
2019-11-05 10:20:00.536683+0800 归档[82834:6785993] 调用了initWithCoder:方法
2019-11-05 10:20:00.536810+0800 归档[82834:6785993] 徐金城,15,180.0
注意:
- 遵守NSCoding协议,并实现该协议中的两个方法。
- 如果是继承,则子类一定要重写那两个方法。因为person的子类在存取的时候,会去子类中去找调用的方法,没找到那么它就去父类中找,所以最后保存和读取的时候新增加的属性会被忽略。需要先调用父类的方法,先初始化父类的,再初始化子类的。
- NSCoding可以存储任意对象到沙盒中,只要遵守他的Coding协议即可. 要将一个自定义的类进行归档,那么类里面的每个属性都必须是可以被归档的,如果是不能归档的类型,我们可以把他转化为NSValue进行归档,然后在读出来的时候在转化为相应的类。
- 保存数据的文件的后缀名可以随意命名。
- 通过plist保存的数据是直接显示的,不安全。通过归档方法保存的数据在文件中打开是乱码的,更安全。
上面的方法我们可以实现把一个对象归档起来, 但是如果我们想把多个对象归档起来应该怎么操作呢?
如何归档多个对象
上面我们使用的是NSKeyedArchiver的类方法. 只能将一个对象归档起来. 如果想要归档多个对象, 需要用NSKeyedArchiver的对象方法, 将各个对象归档到archiver对象对应的data数据中, 再把data数据写入沙盒.
代码如下:
//归档
NSArray *array = [NSArray arrayWithObjects:@"zhangsan",@"lisi",nil];
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
//编码
[archiver encodeObject:array forKey:@"array"];
[archiver encodeInt:100 forKey:@"scope"];
[archiver encodeObject:@"jack" forKey:@"name"];
//结束归档
[archiver finishEncoding];
//路径
NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"array.src"];
//将Data写入
NSLog(@"path=%@",filePath);
BOOL success = [data writeToFile:filePath atomically:YES];
if(success){
NSLog(@"归档成功");
}
//解档
//从路径中initWithContentsOfFile出来data
NSMutableData *data2 = [[NSMutableData alloc] initWithContentsOfFile:filePath];
//创建解归档对象,对data中的数据进行解归档
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data2];
//解档
NSArray *array2 = [unarchiver decodeObjectForKey:@"array"];
NSLog(@"%@",array2);
NSString* name = [unarchiver decodeObjectForKey:@"name"];
NSLog(@"%@",name);
//结束解档
[unarchiver finishDecoding];
打印结果如下:
2019-11-05 11:08:08.056992+0800 归档[83805:6842597] path=/Users/xujincheng/Library/Developer/CoreSimulator/Devices/A3BD602E-4365-4B04-9FA4-61186DCE8ADA/data/Containers/Data/Application/60A736B2-91A1-4B28-B692-574834D77AB6/array.src
2019-11-05 11:08:08.057880+0800 归档[83805:6842597] 归档成功
2019-11-05 11:08:08.058142+0800 归档[83805:6842597] (
zhangsan,
lisi
)
2019-11-05 11:08:08.058214+0800 归档[83805:6842597] jack
四. writeToFile
如果对象是NSString、NSDictionary、NSArray、NSData、 NSNumber等类型,就可以使用writeToFile:atomically:⽅法, 直接将对象写入指定路径下
如果是音频文件,文本文件,视频文件就必须通过路径获取通过NSData的dataWithContentsOfFile接收, 再将NSData写入指定路径
比如:字符串的写入和读取
//将字符串写入到桌面并读取
NSString * str = @"www.baidu.com";
NSError * error;
BOOL isSucess = [str writeToFile:@"/Users/xujincheng/Desktop/baidu.txt" atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (isSucess) {
NSLog(@"写入成功");
}
//注意:如果是其他类型需要使用NSData接收
//删除操作如下
NSFileManager * fm = [[NSFileManager alloc] init];
BOOL isSucess2 = [fm removeItemAtPath:@"/Users/xujincheng/Desktop/baidu.txt" error:&error];
if (isSucess2) {
NSLog(@"删除成功");
}
//删除成功后桌面的文件还在,但是打不开了,应该是没刷新吧
当需要把多个NSData数据拼接成一个数据存储的时候,就要想到使用NSMutableData这个类型
NSString * str1 = @"好好学习";
NSString * str2 = @"天天向上";
NSMutableData * muData = [[NSMutableData alloc] init];
NSData * d1 = [str1 dataUsingEncoding:NSUTF8StringEncoding];
NSData * d2 = [str2 dataUsingEncoding:NSUTF8StringEncoding];
//appendData 能够把nsdata对象加入到 muData对象中
[muData appendData:d1];
[muData appendData:d2];
NSString * path =@"/Users/xujincheng/Desktop/baidu.txt";
BOOL iswriteSucess = [muData writeToFile:path atomically:YES];
if (iswriteSucess) {
NSLog(@"写入成功");
}
运行结果如下, 会把原来的信息都给覆盖掉
五. NSFileManager
NSFileManager是iOS的一个文件操作类, 使用的不多, 简单介绍下
一. 文件信息获取
- 检查文件是否存在
NSString *docPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
NSString *path=[docPath stringByAppendingPathComponent:@"xujincheng"];
NSFileManager *fm = [[NSFileManager alloc] init];
BOOL isDirectory ; //是否是文件夹
BOOL isExists = [fm fileExistsAtPath:path isDirectory:&isDirectory]; //是否存在
//directory目录
if (isExists) {
NSLog(@"存在");
if (isDirectory) {
NSLog(@"是文件夹");
} else {
NSLog(@"是文件");
}
}
/*
2019-11-05 14:28:26.838901+0800 归档[87700:7169471] 存在
2019-11-05 14:28:26.838968+0800 归档[87700:7169471] 是文件
*/
- 获取当前路径下的文件/文件夹目录
//获取当前路径下的文件/文件夹目录
NSArray * array=[fm contentsOfDirectoryAtPath:docPath error:nil];
NSLog(@"%@",array);
/*
(
".DS_Store",
"测试",
xujincheng,
"测试2"
)
*/
- 逐级获取所有子集的目录
//逐级获取所有子集的目录(数组里是子集路径)
NSError *error;
NSArray * array2 = [fm subpathsOfDirectoryAtPath:docPath error:&error];
NSLog(@"%@",array2);
/*
(
".DS_Store",
"测试",
"测试/测试.txt",
xujincheng,
"测试2",
"测试2/.DS_Store",
"测试2/xujincheng2",
"测试2/xujincheng",
"测试2/测试3",
"测试2/测试3/xujincheng5",
"测试2/xujincheng1"
)
*/
- 获取当前路径下文件/文件夹所有属性的值
//获取当前路径下文件/文件夹所有属性的值
NSDictionary * dic = [fm attributesOfItemAtPath:docPath error:&error];
NSLog(@"%@",dic);
/*
{
NSFileCreationDate = "2019-11-05 02:20:00 +0000";
NSFileExtendedAttributes = {
"com.apple.lastuseddate#PS" = <56ddc05d 00000000 3b0c7f16 00000000>;
};
NSFileExtensionHidden = 0;
NSFileGroupOwnerAccountID = 20;
NSFileGroupOwnerAccountName = staff;
NSFileModificationDate = "2019-11-05 02:20:00 +0000";
NSFileOwnerAccountID = 501;
NSFilePosixPermissions = 420;
NSFileReferenceCount = 1;
NSFileSize = 252;
NSFileSystemFileNumber = 8613456470;
NSFileSystemNumber = 16777220;
NSFileType = NSFileTypeRegular;
}
*/
比如获取文件创建日期和大小
NSDate * date = [dic objectForKey:NSFileCreationDate];
NSString * size = [dic objectForKey:NSFileSize];
二. 文件操作
逐级创建文件夹, NO表示只能够创建一级目录
BOOL isCreateSuccess = [fm createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
创建文件
BOOL isCreateSuccess = [fm createFileAtPath:pathTo contents:data attributes:nil];
移动目录移动
BOOL isMoveSuccess = [fm moveItemAtPath:path toPath:pathTo error:&error];
删除目录
BOOL isRemoveSuccess = [fm removeItemAtPath:path error:&error];
拷贝文件目录到其他地方
BOOL isCopySuccess = [fm copyItemAtPath:path toPath:pathTo error:&error];
小练习:
将一个目录剪切到另外一个目录
//1.拿到文件路径
NSString *docPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
NSString *fromPath=[docPath stringByAppendingPathComponent:@"测试2"];
NSString *toPath=[docPath stringByAppendingPathComponent:@"测试"];
//2.得到目录下面的所有文件
NSFileManager *fm = [[NSFileManager alloc] init];
NSArray *subPaths = [fm subpathsAtPath:fromPath];
//3.遍历所有文件,然后执行剪切操作
//在子线程操作
NSInteger count = subPaths.count;
dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t i) {
//3.1 拼接文件的全路径
NSString *fullPath = [fromPath stringByAppendingPathComponent:subPaths[I]];
NSString *toFullPath = [toPath stringByAppendingPathComponent:subPaths[I]];
NSLog(@"%@",fullPath);
//3.2 执行剪切操作
[fm moveItemAtPath:fullPath toPath:toFullPath error:nil];
});
运行结果如下图:
六. KeyChain钥匙串
钥匙串数据存在系统下面的一个数据库里, 所以可以进行应用之间数据交互, 并且应用删除之后数据还在
- 增删改查
增:
- (void)saveDataToKeyChain{
NSMutableDictionary *infoDict = [NSMutableDictionary new];
// 1 存的什么数据类型
/*
kSecClassInternetPassword // 互联网密码
kSecClassGenericPassword // 通用密码
kSecClassCertificate// 证书
kSecClassKey // 密钥
kSecClassIdentity // 身份ID
*/
[infoDict setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
// 2 存的什么数据value
NSString *password = @"abc123";
[infoDict setObject:[password dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)kSecValueData];
// 3 设置查询的条件
[infoDict setObject:@"EOCClassTest" forKey:(id)kSecAttrAccount];
//往系统下面添加一个字典
OSStatus status = SecItemAdd((CFDictionaryRef)infoDict, NULL);
if (status == noErr) {
NSLog(@"success");
}else{
NSLog(@"fail: %d", status);
}
}
删:
- (void)deleteArchData{
NSMutableDictionary *conditionInfo = [NSMutableDictionary new];
// 1 数据类型
[conditionInfo setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
//2 设置条件 (查询)
[conditionInfo setObject:@"EOCClassTest" forKey:(id)kSecAttrAccount];
OSStatus status = SecItemDelete((CFDictionaryRef)conditionInfo);
if (status == noErr) {
NSLog(@"success");
}else{
NSLog(@"fail: %d", status);
}
}
改:
- (void)updateArch{
NSMutableDictionary *conditionInfo = [NSMutableDictionary new];
// 1 数据类型
[conditionInfo setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
//2 设置条件 (查询)
[conditionInfo setObject:@"EOCClassTest" forKey:(id)kSecAttrAccount];
// 更新的数据
NSMutableDictionary *newValueDict = [NSMutableDictionary new];
NSString *newValue = @"987654";
[newValueDict setObject:[newValue dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)kSecValueData];
OSStatus status = SecItemUpdate((CFDictionaryRef)conditionInfo, (CFDictionaryRef)newValueDict);
if (status == noErr) {
NSLog(@"success");
}else{
NSLog(@"fail: %d", status);
}
}
查:
//通过系统提供的接口,操作数据库
- (void)getArchData{
//条件字典
NSMutableDictionary *conditionInfo = [NSMutableDictionary new];
// 1 数据类型
[conditionInfo setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
//2 设置条件 (查询)
[conditionInfo setObject:@"EOCClassTest" forKey:(id)kSecAttrAccount];
// 3 返回的数据格式
[conditionInfo setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; // kSecReturnRef
CFDataRef data = NULL; //返回的值
OSStatus status = SecItemCopyMatching((CFDictionaryRef)conditionInfo, (CFTypeRef*)&data);
if (status == noErr) {
NSLog(@"success");
}else{
NSLog(@"fail: %d", status);
}
NSData *backData = (__bridge NSData*)data;
NSLog(@"Get:::%s", [backData bytes]);
}
实现如上代码即可实现钥匙串的增删改查, 但是我试了,上面代码不全对
当报错的时候可根据错误码去SecBase.h搜索错误, 常见的错误如下- 应用之间数据交互
① 在当前应用存储到钥匙串
- (void)saveDataMultiApp{
NSMutableDictionary *infoDict = [NSMutableDictionary new];
// 1 数据类型
/*
kSecClassInternetPassword // 互联网密码
kSecClassGenericPassword // 通用密码
kSecClassCertificate// 证书
kSecClassKey // 密钥
kSecClassIdentity // 身份ID
*/
[infoDict setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
// 2 数据value
NSString *password = @"eocApp123";
[infoDict setObject:[password dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)kSecValueData];
/*
跨应用数据
kSecAttrAccessGroup组的条件key
1 配置groupkey (机构号 + bundle identifier)
2 配置一个plist文件(把groupkey放进去, 如下图)
3 配置工程的 coding entitlment, 如下图
*/
//机构号就是发布证书里面的用户ID或者组织单位
[infoDict setObject:@"Y9PY69PRSB.com.test.EocClass" forKey:(id)kSecAttrAccessGroup];
// 3 设置条件 (查询)
[infoDict setObject:@"EOCClassMutiApp" forKey:(id)kSecAttrAccount];
OSStatus status = SecItemAdd((CFDictionaryRef)infoDict, NULL);
if (status == noErr) {
NSLog(@"success");
}else{
NSLog(@"fail: %d", status);
}
}
跨应用数据
kSecAttrAccessGroup组的条件key
1 配置groupkey (机构号 + bundle identifier)机构号就是发布证书里面的用户ID或者组织单位
2 配置一个plist文件(把groupkey放进去, 如下图)
3 配置工程的 coding entitlment, 如下图
② 在其他应用读取
我们重新创建一个应用, 将上面的plist文件拖到本项目, 并且配置plist文件路径, 用如下代码读取
- (void)getArchData{
NSMutableDictionary *conditionInfo = [NSMutableDictionary new];
// 1 数据类型
[conditionInfo setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
//2 设置条件 (查询)
[conditionInfo setObject:@"EOCClassMutiApp" forKey:(id)kSecAttrAccount];
// 3 返回的数据格式
[conditionInfo setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
CFDataRef data = NULL;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)conditionInfo, (CFTypeRef*)&data);
if (status == noErr) {
NSLog(@"success");
}else{
NSLog(@"fail: %d", status);
}
NSData *backData = (__bridge NSData*)data;
NSLog(@"Get:::%s", [backData bytes]);
}
可以发现, 只要把一个应用的groupkey告诉你, 你就可以读取到它存在钥匙串里面的东西.
这种方式不用走网络. 用的比较少, 作为了解
Demo地址:keyChain