App开发流程之数据持久化和编译静态链接库

先记录数据持久化。

iOS客户端提供的常用数据持久化方案:NSUserDefaults代表的用户设置,NSKeydArchiver代表的归档,plist文件存储,SQLite数据库(包括上层使用的Core Data,FMDB)。

每种方案都有各自的应用场景和范围,不能一概而论。不过可以大致以数据储存量和复杂度来区别。

除了以上提到的方案,再记录一种方案:LevelDB代表的键值对数据库。

NSUserDefaults常用方法:

1.可以使用标准用户设置[NSUserDefaults standardUserDefaults],也可以通过init相关方法初始化新的用户设置

2.像使用字典一样获取、设置、移除键值对

3.synchronize方法已经不建议使用

Plist文件存储:

1.代码读取应用内已经存在的plist文件,得到一个字典

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"plist"];

NSMutableDictionary *dic = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];

2.修改数据后,保存或者创建plist文件

[dic writeToFile:filePath atomically:YES];

NSKeydArchiver和NSKeyedUnarchiver:

1.归档有一个类方法:+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;

解档有一个类方法:+ (nullable id)unarchiveObjectWithFile:(NSString *)path;

 可以直接对某一个对象进行归档和解档。

2.但如果需要对多个键值对进行操作,建议使用如下方法:

+ (void)archiveDataWithDictionary:(NSDictionary *)dic filename:(NSString*)filename archiveSuccessBlock:(archiveSuccessBlock)archiveSuccessBlock
{
    NSString *fullPath = [self getAppArchivedFileFullPathWithName:filename];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSMutableData *data = [NSMutableData data];
        NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

        NSArray *keyArray = [NSArray arrayWithArray:[dic allKeys]];
        [archiver encodeObject:keyArray forKey:filename];

        for (NSString *key in keyArray) {
            NSObject *object = [dic objectForKey:key];
            [archiver encodeObject:object forKey:key];
        }

        [archiver finishEncoding];
        [data writeToFile:fullPath atomically:YES];

        if (archiveSuccessBlock) {
            archiveSuccessBlock();
        }
    });
}

+ (NSDictionary *)unarchiveDataWithFilename:(NSString *)filename
{
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];

    NSData *data = [[NSData alloc] initWithContentsOfFile:[self getAppArchivedFileFullPathWithName:filename]];
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

    NSArray *keyArray = [NSArray arrayWithArray:[unarchiver decodeObjectForKey:filename]];

    for (NSString *key in keyArray) {
        NSObject *object = [unarchiver decodeObjectForKey:key];
        [dic setObject:object forKey:key];
    }

    [unarchiver finishDecoding];

    return dic;
}

归档的initForWritingWithMutableData和finishEncoding,解档的initForReadingWithData和finishDecoding需要成对出现。

SQLite数据库:

只要使用过SQL Server和MySQL之类的关系型数据库,就可以轻松使用,只是底层的sql语言不太人性化,所以普遍采用了上层的Core Data或者FMDB类库。

文件操作:

首先关注方法:FOUNDATION_EXPORT NSArray<NSString *> *NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);第一个枚举参数表示文件目录,第二个表示范围域,第三个参数表示是否补充完整的相对路径

1.得到当前用户的doc文件根目录:

 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *rootPath = paths[0];

2.补充子文件路径

NSString *fullPath = [rootPath stringByAppendingPathComponent:filename];

3.文件操作,主要使用[NSFileManager defaultManager]单例对象

+ (void)createArchivedRootFile
{
    NSString *rootPath = [self getAppArchivedFilesRootPath];

    if (![[NSFileManager defaultManager] fileExistsAtPath:rootPath]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:rootPath withIntermediateDirectories:YES attributes:nil error:nil];
    }
}

+ (void)clearArchivedFileWithName:(NSString *)filename
{
    NSString *rootPath = [self getAppArchivedFilesRootPath];
    NSString *fullPath = [rootPath stringByAppendingPathComponent:filename];

    if ([[NSFileManager defaultManager] fileExistsAtPath:fullPath]) {
        [[NSFileManager defaultManager] removeItemAtPath:fullPath error:nil];
    }
}

除了以上记录方案,再记录一下使用键值对数据库LevelDB的经历。

当数据量并不是很大,但是又需要数据库存储和操作时候,键值对数据库是首选。源自Google的LevelDB是其中的明星。

之前遇到客户端存储省市区地址数据的需求,并当用户选择地址时候读取相关数据。如果每次都完整读取省市区数据,占用内存既大又没有必要,因为用户很可能只会选择一个省下的一个市的一个区。

如果将省市区数据拆分为若干键值对,并且建立某种链式关系,就可以将数据以键值对分散存储于某个地方,并且快速读取需要数据。

1.第一层级只有一个键值对,key为固定值,value为全部省的名称数组

2.第二层级键值对数量为省数量,key为省名称,value为市名称数组

3.第三层级键值对数量为市数量,key为“省名称.市名称”,value为区名称数组

4.。。。。。

如上,数据全部以键值对分散存储于LevelDB中,只要知道key规则和名称,就可以快速取到对应数据,而优秀的IO保证了性能表现。

这是之前记录的一篇关于LevelDB的文章,可以先参考一下:http://www.cnblogs.com/A-Long-Way-Chris/p/4864573.html

编译静态链接库

正好以LevelDB为案例。先前往下载C++源代码: https://github.com/google/leveldb

使用Xcode创建静态链接库

1.新建项目,选择类型

2.设置项目Build Phases,点击区域左上角加号,选择添加Headers Phase

3.点击加号,添加需要公开暴露的头文件,然后从Project栏拖拽到Public栏

4.切换真机和模拟器,分别编译成功后,右键Products目录下的libleveldb.a,在Finder中查看

5.在终端程序中cd到该目录,输入如下指令,即可导出同时支持真机和模拟器运行的静态链接库

lipo -create Debug-iphoneos/libleveldb.a Debug-iphonesimulator/libleveldb.a -output libleveldb.a

使用命令行,通过Makefile编译LevelDB的静态链接库

1.解压下载包后,使用Visual Studio Code之类的文本编辑器打开目录下Makefile

2.修改Makefile中的CXXFLAGS,添加指令 -fembed-bitcode,保存

3.在终端中,cd到LevelDB目录,输入指令:CXXFLAGS=-miphoneos-version-min=7.0 make PLATFORM=IOS

表示生成iOS版本的静态链接库,支持最低版本为7.0(该设置保证在模拟器上可以正常运行)。

如果提示permission denied,则在上述指令前加上sudo,最终为:sudo CXXFLAGS=-miphoneos-version-min=7.0 make PLATFORM=IOS,然后输入密码回车即可。

4.说明一下,如果跳过步骤1和2,最后生成的LevelDB静态链接库不支持bitcode,可以看到体积相差还是比较大的,按需编译

将.a文件和include目录下的头文件加入项目即可正常使用。

1.如果遇到提示某头文件找不到,请检查项目配置中,Header Search Paths是否有配置缺失

2.如果使用不支持bitcode的版本,需要在build setting中将enable bitcode设置为NO。该设置对其他类库要求一样

为了便于使用OC编程,还引入了另一个类库,对LevelDB的代码进行了OC封装,地址:https://github.com/matehat/Objective-LevelDB

我在base项目中,增加了LevelDBHelper工具类,进一步对调用代码进行了封装,操作更简单安全。

base项目已更新:[email protected]:ALongWay/base.git

时间: 2024-12-21 06:13:26

App开发流程之数据持久化和编译静态链接库的相关文章

20个可以帮你简化iOS app开发流程的工具

这里推荐20个可以帮你简化iOS app开发流程的工具.很多开发者都使用过这些工具,涉及原型和设计.编程.测试以及最后的营销,基本上涵盖了整个开发过程. 原型和设计 有了一个很好的创意后,你要做的不是立刻编程,而是设计UI和创建原型,这样你才能知道app如何运行,根据用户体验需要做哪些调整. App Cooker AppCooker 不仅是一个创建原型的优秀工具,它提供的许多功能还可以帮助你将程序发布到App store中.它集成了Dropbox,Box.net和photo roll,你可以直接

iOS开发之工具篇-20个可以帮你简化移动app开发流程的工具

本文转载至 http://www.cocoachina.com/ios/20130402/5926.html 如果想进入移动app开发这个领域,你总能从别的开发者或者网上或者书上找到各种各样的方法和工具,对于新手来说,还没有摸清门路就已经陷入迷茫了.这里推荐20个可以帮你简化app开发流程的工具.很多开发者都使用过这些工具,涉及原型和设计.编程.测试以及最后的营销,基本上涵盖了整个开发过程. 原型和设计 有了一个很好的创意后,你要做的不是立刻编程,而是设计UI和创建原型,这样你才能知道app如何

app开发流程有哪些

app开发流程是需求方和供求方相互协调的过程,一般分为需求分析.功能设计.功能实现.项目测试.上线等几个步骤,下面我们就来一起看看ytkah团队进行app开发各个流程主要做哪些事情,让您对app开发设计有一个更全面的了解,更放心地将项目交予我们来为您实施. app开发流程1. 需求分析首先要确定项目整体方案,整个项目的规划,大体框架,做成文档展现出来,以便大家提意见和更好的改进.也就是说首先要确立产品原型,进入项目评估阶段.经过反复确认,最终形成产品脑图和完整的需求文档. app开发流程2. 功

GCC编译过程与动态链接库和静态链接库

1. 库的介绍 库是写好的现有的,成熟的,可以复用的代码.现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行.库有两种:静态库(.a..lib)和动态库(.so..dll). windows上对应的是.lib .dll linux上对应的是.a .so 在这里先介绍下Linux下的gcc编译的几个选项 g++ -c hellospeak.cpp 会将hellospeak.cpp 选

静态编译、动态编译、静态链接库和动态链接库理解

1.静态编译:编译器在编译可执行文件时,把需要用到的对应动态链接库(.so或.ilb)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行时不需要依赖于动态链接库. 2.动态编译: 动态编译的可执行文件需要附带一个的动态链接库,在执行时,需要调用其对应动态链接库中的命令.所以其优点一 方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源.缺点一是哪怕是很简单的程序,只用到了链接 库中的一两条命令,也需要附带一个相对庞大的链接库:二是如果其他计算机上没有安装对应的运行库,

linux下的动态链接库和静态链接库到底是个什么鬼?(二)动态链接库的编译与使用

上一篇文章里讲解了linux下静态链接库的编译与使用,下面我们来聊聊动态链接库的编译与使用方法. 所谓动态链接库,也就是说编译的时候不会真的把你引用到的库给编到你的执行程序里,而是在执行时候才会去加载相关的库,所有用到此库的程序可以共享一份代码,这样带来的好处是可执行程序所占的空间变小了,同时,如果库需要升级,你并不需要重新编译你的程序,只要把相关的库升级即可. 接下来我们来看看动态链接库的编译与使用方法,代码还是和上文中的一样,分别为?sum.c, sum.h, 和main.c, 在linux

g++ 编译动态链接库和静态链接库

现在我有hello1.cpp和hello2.cpp两个文件,现在我要生成动态链接库libhello.so和静态链接库libhello.a.以下为步骤: 1.生成动态链接库: g++ -m32 hello1.cpp hello2.cpp -fPIC -shared -o ../lib/linux32/libhello.so -m32为生成32位的动态链接库,-m64位生成64位的动态链接库. 2.生成静态链接库: g++ -m32 hello1.cpp -c -o hello1.o  //编译he

GCC 编译使用动态链接库和静态链接库的方法

1 库的分类 根据链接时期的不同,库又有静态库和动态库之分. 静态库是在链接阶段被链接的,所以生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行. 有别于静态库,动态库的链接是在程序执行的时候被链接的.所以,即使程序编译完,库仍须保留在系统上,以供程序运行时调用. 2 静态库和动态库的比较 链接静态库其实从某种意义上来说也是一种粘贴复制,只不过它操作的对象是目标代码而不是源码而已.因为静态库被链接后库就直接嵌入可执行文件中了,这样就带来了两个问题. 首先就是系统空间被浪费了.

[转载]GCC 编译使用动态链接库和静态链接库--及先后顺序----及环境变量设置总结

来自http://blog.csdn.net/benpaobagzb/article/details/51364005 GCC 编译使用动态链接库和静态链接库 1 库的分类 根据链接时期的不同,库又有静态库和动态库之分. 静态库是在链接阶段被链接的(好像是废话,但事实就是这样),所以生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行. 有别于静态库,动态库的链接是在程序执行的时候被链接的.所以,即使程序编译完,库仍须保留在系统上,以供程序运行时调用.(TODO:链接动态库时链