最近在项目中本地化存储的时候用NSUserDefaults来做本地化储存,随着项目越来越大,需要存入的数据也越来越多,比如各种判断条件,是否第一次登录,是否第一次安装等等,这些条件多了,觉得不太好管理,就想着统一用一个 plist 文件来管理这些。
/**
操作plist
@param pathBlock 设置路径
@param handleBlock 数据操作
@return 这个闭包是用来存储更新到本地,如果这是查询不需要更新本地,可以不调用这个返回值
*/
-(void(^)(void))nativeConfigOperate:
(NSArray<NSString *>*(^)(void))pathBlock
handle:(void(^)(NSMutableDictionary *valueLastDic, id value))handleBlock;
基于高阶函数的启发设计了这么一个类出来管理本地的 plist 文件,
这个方法是线程安全的,即使在多线程中操作访问也是没问题。
pathBlock
:设置 plist 文件中的路径,如下图查找BaseConfig中的key,则返回@[@"BaseConfig",@"key"]
![](https://img.haomeiwen.com/i5268395/e217c8fa213b00e4.png)
handleBlock
:返回查询到的数据,
如果路径不对:
- A:在最后一个路径之前就已经有路径不存在,那么返回valueLastDic,value两个参数都是 nil,并且最后writeToFile 依然是之前已经存在的 plist 文件,plist 文件不会改变。
- B:如果是之前的路径没有问题,路径都是存在的,只是最后一个路径不存在,那么返回的valueLastDic为查询路径的上一个dic,那工程里面的 InfoConfig.plist 举例如:@[@"c"],那么valueLastDic就是整个plist字典,如果:@[@"BaseConfig",@"aa"],那么valueLastDic就是路径 BaseConfig 下的字典。 增删改这些字典最后writeToFile是操作过的数据.
返回值 block
:调用这个闭包用来执行writeToFile存储更新到本地,如果这是查询不需要更新本地,可以不执行这个block。
关于嵌套的问题
,如果是两个嵌套返回的valueLastDic是兄弟关系的话,最后存储的数据都是正确的,如果是父子层级的以最后调用返回值 block的结果为准,会存在覆盖前值的问题。
最后贴上实现代码。点我下载,喜欢的点个赞
-(void(^)(void))nativeConfigOperate:(NSArray<NSString *>*(^)(void))pathBlock handle:(void(^)(NSMutableDictionary *lastDic, id value))handleBlock
{
NSAssert(pathBlock != nil, @"路径不能为空");
dispatch_semaphore_wait(signal, overTime);
///沙盒 plist 路径,不要放在工程目录下,没有权限去修改。
NSString *infoplistPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"config/InfoConfig.plist"];
NSArray <NSString *>* keypathArr = pathBlock();
if (keypathArr.count == 0) {
return ^{ };
}
//plist 对象
NSMutableDictionary *bigDic = [NSMutableDictionary dictionaryWithContentsOfFile:infoplistPath];
NSLog(@"lastDic 初始化===%@",bigDic);
//最后一个 key 所在的 dictionary,暴露给其他调用类,可以用来增删改查操作
NSMutableDictionary *lastDic = bigDic;
id lastData = nil;
for (int i=0; i<keypathArr.count; i++) {
if (i==keypathArr.count-1) {
//根据keypathArr路径查询最后一个 key 对应的数据
lastData = lastDic[keypathArr[i]];
break;
}
lastDic = lastDic[keypathArr[i]];
}
WeakObj(keypathArr);
WeakObj(infoplistPath);
WeakObj(lastDic);
WeakObj(bigDic);
void(^handleFinishBlock)(void) = ^{
/*
optimize:
应该采用链表结构设计,
*/
#warning 最结束处一定要调用线程解锁
//在返回 block 的时候一定要解锁,否则会阻塞线程
dispatch_semaphore_wait(signal, overTime);
//用 strong 修饰防止weak 修饰的对象释放,而 block 对其内部创建的对象并不会再一次的强引用
StrongObj(keypathArr_W);
StrongObj(infoplistPath_W);
StrongObj(lastDic_W);
StrongObj(bigDic_W);
if (keypathArr_WS.count == 1) {//第一层
bigDic_WS = lastDic_WS;
}else {//第二层及以上
NSMutableDictionary *tempDic = bigDic_WS;
NSInteger count = keypathArr_WS.count-1;
//for 遍历,后期我将尝试用节点的方式来设计算法
for (int i=0; i<count; i++) {
if (i == count-1) {
if (i == 0) {
//最终遍历会执行到这里结束
[bigDic_WS setValue:lastDic_WS forKey:keypathArr_WS[i]];
break;
}
[tempDic setValue:lastDic_WS forKey:keypathArr_WS[i]];
count = i;
i = -1;
lastDic_WS = tempDic;
tempDic = bigDic_WS;
continue;
}
tempDic = tempDic[keypathArr_WS[i]];
}
}
/*
路径数组中包含不存在的路径
1、最后一个 key 不存在,则依然会返回正确的 lastDic ,只是返回的 value 为空而已。
2、之前的 key 不存在,那么存入的依然是之前本地已经存在的数据,不会改变
*/
NSLog(@"bigDic_WSKVC 赋值后===%@",bigDic_WS);
BOOL storage = [bigDic_WS writeToFile:infoplistPath_WS atomically:YES];
NSLog(@"储存==%@---%@",storage?@"成功":@"失败",keypathArr);
#warning 最结束处一定要调用线程解锁
//线程解锁
dispatch_semaphore_signal(signal);
};
handleBlock(lastDic, lastDic[keypathArr[keypathArr.count-1]]);
dispatch_semaphore_signal(signal);
return handleFinishBlock;
}
最后说一句,链式点语法调用爽爆了。
网友评论