一. 沙盒
每个iOS应用都有⾃己的应⽤沙盒(应用沙盒就是文件系统目录)与其他文件系统隔离,应⽤必须待在⾃己的沙盒里,其他应用不能访问该沙盒(在iOS8中已经开放访问)。
应⽤沙盒的文件系统⽬录,如下图所示:可以看出,沙盒里面有四个文件夹,分别是Documents,Lirary,SystemData,tmp。Lirary里面又有Caches,Preferences两个文件夹。
1. Documents
默认是备份的(使用itunes或者iCloud会备份的时候)。
大文件尽量不要存储在这个目录下(视频文件)如果不做任何处理,审核,检查出来,就会被拒。
如果不想被拒,下面的操作二选一:
- 文件非备份操作设置
因为Documents文件夹默认是备份的,我们需要写以下代码:
// 设置非备份
//#import <sys/xattr.h>
- (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
如果想要获取主Bundle的图片直接:
_imageView.image = [UIImage imageNamed:@"2.png"];
获取主Bundle资源文件路径的方法:
//获取资源路径
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@”apple” ofType:@”png”];
//根据路径得到资源
UIImage *appleImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
//代码中的mainBundle类方法用于返回一个代表应用程序包的对象
② 新添加的Bundle
但是如果想要获取新添加的Bundle的图片,就要先拿到新添加的Bundle,如下:
//获取Bundle路径
NSString *boudlePath = [[NSBundle mainBundle] pathForResource:@"EOCBundle" ofType:@"bundle"];
//根据Bundle路径获取Bundle
NSBundle *eocBoundle = [NSBundle bundleWithPath:boudlePath];
//根据Bundle获取图片路径
NSString *imagePath = [eocBoundle pathForResource:@"11" ofType:@"png"];
//根据图片路径得到图片资源
_imageView.image = [UIImage imageWithContentsOfFile:imagePath];
上面方式稍微有点麻烦,我们经常这样获取:
UIImage *ima = [UIImage imageNamed:@"StaticDemo2.bundle/美女2.png"];
补充:
项目中黄色文件夹和蓝色文件夹的区别:
-
我们项目中一般创建的都是黄色文件夹,黄色的是参与编译的,比如:黄色.png
-
项目中的蓝色文件夹是资源文件夹,不参与编译,比如:蓝色.png
6. 获取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。
简单使用:
//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中(不推荐)。
三. NSKeyedUnarchiver(归档)
- 使用plist进行数据存储和读取,只适用于系统自带的一些常用类型才能用,且必须先获取路径相对麻烦。
- 对于NSUserDefaults,主要用于存储应用的设置信息,而且它的本质还是plist文件,plist都能直接打开,相对不安全。
① 归档一个对象
这两者都有一个致命的缺陷,只能存储常用的类型(plist文件支持的类型)所以我们要介绍归档,归档可以实现把自定义的对象存放在文件中
先自定义一个对象,对象遵守NSCoding协议,并且实现两个协议方法,如果不实现两个协议方法,会报错。
@interface Person : NSObject<NSCoding>
//姓名
@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的一个文件操作类,使用的不多,简单介绍下。
为了测试,我们在沙盒里面创建一些文件和文件夹,如下:1. 文件信息获取
① 检查文件是否存在
//directory目录
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]; //是否存在
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];
2. 文件操作
//逐级创建文件夹, 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钥匙串
钥匙串数据存在系统下面的一个数据库里,所以可以进行应用之间数据交互,并且应用删除之后数据还在。
1. 增删改查
增:
- (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文件搜索错误,常见的错误如下:
2. 应用之间数据交互
① 在当前应用存储到钥匙串
- (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