iOS数据持久化方式及class_copyIvarList与class_copyPropertyList的区别
iOS数据持久化方式:
plist文件(属性列表)
preference(偏好设置)
NSKeyedArchiver(归档)
SQLite3
CoreData
沙盒:iOS程序默认情况下只能访问自己的程序目录,这个目录被称为沙盒。
沙盒目录结构:
Documents
Library->Caches Preferences
tmp
获取沙盒路径最方便的方法:
NSString *sandBoxPath = NSHomeDirectory();
然后在其路径后追加Documents或tmp或Library
各个目录适合存储的文件:
Documents目录存储重要文件,iTunes同步时会同步该文件夹内容,一般用来存储数据库文件
Library->Preferences通常保存应用配置信息,iTunes同步时也会同步该文件夹内容
Library->Caches:iTunes不会同步该文件夹内容,适合存储体积大,不需要备份的非重要数据。
tmp:iTunes不会同步该文件夹内容,系统可能在程序没运行时就删除该目录下的文件,所以适合保存应用中的一些临时文件,用完就删除。
1.plist文件是将某些特定的类,通过XML文件的格式保存在目录中。
包含以下几个类:
NSArray
NSDictionary
NSString
NSData
NSNumber
NSDate
这里先抛出一个疑问:比如我先往plist文件里写入一个字典,然后我想接着写入一个数组,但是实际操作中,后面写入的数据总是会覆盖前面写入的数据,有什么解决方案吗?
2.NSUserDefaults
一定要记得synchronize,存储的值都保存在Library/Preferences里的以程序bundle id命名的plist文件里。
3.NSKeyedArchiver,要想使用这种方式存储,则必须遵循NSCoding协议
@protocol NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (instancetype)initWithCoder:(NSCoder *)aDecoder;
我们一般常见的的序列化方式如下图:
以上是一个简单的编解码示意,但是可能会有一些缺陷:
1.若需要实现序列化的类有很多属性,那是不是就要写很多代码?
2.若该类不是直接继承自NSObject,而是有其他父类,那就需要先实现父类的序列化
这里需要注意:序列化的范围不仅仅是自身类的变量,还要把除NSObject外的所有父类的变量都进行序列化
其实编解码的核心就是:遍历该类的属性变量、实例变量及其父类的属性变量(父类的实例变量为其私有,我们如果对其进行编解码会崩溃),这时候就用到运行时的api了
敲重点:
Class cls = [self class];
BOOL isSelfClass = (cls == [self class]);
objc_property_t *propertyList = isSelfClass ? NULL : class_copyPropertyList(cls, &propertyCount);
Ivar *ivarList = isSelfClass ? class_copyIvarList(cls, &iVarCount) :NULL;
解释一下,首先判断是不是本类,如果是本类,就使用class_copyIvarList获取属性变量和实例变量;
如果是父类,就使用class_copyIvarList获取父类的属性变量。
//解码
- (id)initWithCoder:(NSCoder *)coder
{
Class cls = [self class];
while (cls != [NSObject class]) {
BOOL isSelfClass = (cls == [self class]);
unsigned int iVarCount = 0;
unsigned int propertyCount = 0; objc_property_t *propertyList = isSelfClass ? NULL : class_copyPropertyList(cls, &propertyCount);
Ivar *ivarList = isSelfClass ? class_copyIvarList(cls, &iVarCount) :NULL; unsigned int finalCount = isSelfClass ? iVarCount : propertyCount; for (int i = 0; i < finalCount; i++) {
const char * varName = isSelfClass ? ivar_getName(*(ivarList + i)) :property_getName(*(propertyList + i));//取得变量名字,将作为key
NSString *key = [NSString stringWithUTF8String:varName];
//decode
id value = [coder decodeObjectForKey:key];//解码
NSArray *filters = @[@"superclass", @"description", @"debugDescription", @"hash"];
if (value && [filters containsObject:key] == NO) {
[self setValue:value forKey:key];//使用KVC强制写入到对象中
}
}
free(ivarList);//记得释放内存
free(propertyList);
cls = class_getSuperclass(cls);
} return self; }
//编码
- (void)encodeWithCoder:(NSCoder *)coder
{
Class cls = [self class];
while (cls != [NSObject class]) {
BOOL isSelfClass = (cls == [self class]);
unsigned int varCount = 0;
unsigned int properCount = 0;
Ivar *ivarList = isSelfClass ? class_copyIvarList([self class], &varCount) : NULL;
objc_property_t *propertyList = isSelfClass ? NULL : class_copyPropertyList(cls, &properCount); unsigned int finalCount = isSelfClass ? varCount : properCount;
for (int i = 0; i < finalCount; i++) {
const char *varName = isSelfClass ? ivar_getName(*(ivarList + i)) : property_getName(*(propertyList + i));
NSString *key = [NSString stringWithUTF8String:varName];
id varValue = [self valueForKey:key];//使用KVC获取key对应的变量值
NSArray *filters = @[@"superclass", @"description", @"debugDescription", @"hash"];
if (varValue && [filters containsObject:key] == NO) {
[coder encodeObject:varValue forKey:key];
}
}
free(ivarList);
free(propertyList);
cls = class_getSuperclass(cls);
} }
另外,为一个类添加描述也可以同理进行处理,这样就可以很直观的看出容器里装的model类是怎样子的了:
/* 用来打印本类的所有变量(成员变量+属性变量),所有层级父类的属性变量及其对应的值 */
- (NSString *)description
{
NSString *despStr = @"";
Class cls = [self class];
while (cls != [NSObject class]) {
/*判断是自身类还是父类*/
BOOL bIsSelfClass = (cls == [self class]);
unsigned int iVarCount = 0;
unsigned int propVarCount = 0;
unsigned int sharedVarCount = 0;
Ivar *ivarList = bIsSelfClass ? class_copyIvarList([cls class], &iVarCount) : NULL;/*变量列表,含属性以及私有变量*/
objc_property_t *propList = bIsSelfClass ? NULL : class_copyPropertyList(cls, &propVarCount);/*属性列表*/
sharedVarCount = bIsSelfClass ? iVarCount : propVarCount; for (int i = 0; i < sharedVarCount; i++) {
const char *varName = bIsSelfClass ? ivar_getName(*(ivarList + i)) : property_getName(*(propList + i));
NSString *key = [NSString stringWithUTF8String:varName];
/*valueForKey只能获取本类所有变量以及所有层级父类的属性,不包含任何父类的私有变量(会崩溃)*/
id varValue = [self valueForKey:key];
NSArray *filters = @[@"superclass", @"description", @"debugDescription", @"hash"];
if (varValue && [filters containsObject:key] == NO) {
despStr = [despStr stringByAppendingString:[NSString stringWithFormat:@"%@: %@n", key, varValue]];
}
}
free(ivarList);
free(propList);
cls = class_getSuperclass(cls);
}
return despStr;
}
然后把以上方法用宏定义起来,放进一个header.h里,需要对哪个类进行编解码,就把header.h导进去。
4.sqlite
说到本博客的关键地方了,以上数据存储方法均是覆盖存储!!!如果想要追加一条数据,就必须把整个文件里的数据读出来,然后修改数据后再把数据写入,所以不适合存储大量数据。这时候sqlite就登场了
SQLite3:
可存储数据类型:
integer:整数
real:实数(浮点数)
text:文本字符串
blob:二进制数据,比如图片,文件之类的
数据库、删、改、查,就查询是最特殊的,需要一个结果集来承载。
每次进行任何操作之前,都不要忘了打开数据库和关闭数据库。
在iOS中要使用SQLite3,需要添加库文件:libsqlite3.tbd,这是一个C语言的库,所以直接使用SQLite3还是比较麻烦的。
常用API:
sqlite3_open(打开数据库)、sqlite3_close(关闭数据库)、sqlite3_exec(执行增删改)、sqlite3_prepare_v2(检查sql语句的合法性)、sqlite3_step(逐行获取查询结果,直到最后一条记录)、sqlite3_column_XXX(获取查询结果里的某个字段内容,XXX为表字段类型)
#pragma mark 创建数据库并创建表
- (void)createDB {
NSString *dbPath = [[self getDocumentsPath]stringByAppendingPathComponent:@"person.sqlite"];
NSInteger openResult = sqlite3_open(dbPath.UTF8String, &_sqlite3);
if (openResult == SQLITE_OK) {
NSLog(@"数据库打开成功");
char *errmsg = NULL;
sqlite3_exec(_sqlite3, "create table if not exists t_person(id integer primary key autoincrement,name text,age integer,gender boolean)", NULL, NULL, &errmsg);
if (errmsg) {
NSLog(@"创建表失败");
} else {
NSLog(@"创建表成功");
sqlite3_close(_sqlite3);
}
} else {
NSLog(@"数据库打开失败");
}
}
#pragma mark 插入数据
- (void)insertDataToDB {
//在进行插入操作之前,要先进行打开数据库
//sqlite3_exec可执行除了查询外的其他所有操作,增,删,改
NSString *dbPath = [[self getDocumentsPath]stringByAppendingPathComponent:@"person.sqlite"];
NSInteger openResult = sqlite3_open(dbPath.UTF8String, &_sqlite3); if (openResult == SQLITE_OK ) {
for (NSInteger i = 0; i < 100; i ++) {
NSString *insertSql = [[NSString alloc]initWithFormat:@"insert into t_person (name,age,gender) values ('%@','%ld','%d')",@"lvjiazhen",(long)(i + 2),0];
char *errmsg = NULL;
sqlite3_exec(_sqlite3, insertSql.UTF8String, NULL, NULL, &errmsg);
if (errmsg) {
NSLog(@"第%ld条语句插入失败",(long)i);
} else {
NSLog(@"第%ld条语句插入成功",(long)i);
}
}
//关闭数据库
sqlite3_close(_sqlite3);
} }
#pragma mark 查询数据
- (void)readDataFromDB {
// sqlite3_prepare_v2() : 检查sql的合法性
//
// sqlite3_step() : 逐行获取查询结果,不断重复,直到最后一条记录
//
// sqlite3_coloum_xxx() : 获取对应类型的内容,iCol对应的就是SQL语句中字段的顺序,从0开始。根据实际查询字段的属性,使用sqlite3_column_xxx取得对应的内容即可。
//
// sqlite3_finalize() : 释放stmt
NSString *dbPath = [[self getDocumentsPath]stringByAppendingPathComponent:@"person.sqlite"];
NSInteger openResult = sqlite3_open(dbPath.UTF8String, &_sqlite3);
if (openResult == SQLITE_OK) {
NSMutableArray *mutArray = [NSMutableArray new];
char *selectSql = "select name,age from t_person";
sqlite3_stmt *stmt;
int result = sqlite3_prepare_v2(_sqlite3, selectSql, -1, &stmt, NULL); if (result == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
char *name = (char *)sqlite3_column_text(stmt, 0);//这里的序列号0或1对应的是selectSql里的列号,而不是字段在表里的列号
NSInteger age = sqlite3_column_int(stmt, 1);
PersonModel *person = [PersonModel new];
person.name = [NSString stringWithUTF8String:name];
person.height = age;
[mutArray addObject:person];
}
//释放结果集
free(stmt);
//关闭数据库
sqlite3_close(_sqlite3);
} NSLog(@"%@",mutArray);
}
}
FMDB有三个最主要的类:
FMDatabase、FMResultSet、FMDatabaseQueue
FMDatabaseQueue:在不同的线程同时进行读写,这时候必须进行锁操作。
iOS数据持久化方式及class_copyIvarList与class_copyPropertyList的区别的更多相关文章
- iOS -数据持久化方式-以真实项目讲解
前面已经讲解了SQLite,FMDB以及CoreData的基本操作和代码讲解(CoreData也在不断学习中,上篇博客也会不断更新中).本篇我们将讲述在实际开发中,所使用的iOS数据持久化的方式以及怎 ...
- iOS中的数据持久化方式
iOS中的数据持久化方式,基本上有以下四种:属性列表.对象归档.SQLite3和Core Data. 1.属性列表 涉及到的主要类:NSUserDefaults,一般 [NSUserDefaults ...
- iOS开发中的4种数据持久化方式【二、数据库 SQLite3、Core Data 的运用】
在上文,我们介绍了ios开发中的其中2种数据持久化方式:属性列表.归档解档.本节将继续介绍另外2种iOS持久化数据的方法:数据库 SQLite3.Core Data 的运 ...
- iOS 数据持久化(扩展知识:模糊背景效果和密码保护功能)
本篇随笔除了介绍 iOS 数据持久化知识之外,还贯穿了以下内容: (1)自定义 TableView,结合 block 从 ViewController 中分离出 View,轻 ViewControll ...
- iOS开发笔记-swift实现iOS数据持久化之归档NSKeyedArchiver
IOS数据持久化的方式分为三种: 属性列表 (plist.NSUserDefaults) 归档 (NSKeyedArchiver) 数据库 (SQLite.Core Data.第三方类库等 归档(又名 ...
- 四种数据持久化方式(下) :SQLite3 和 Core Data
在上文,我们介绍了iOS开发中的其中2种数据持久化方式:属性列表.归档解档. 本节将继续介绍另外2种iOS持久化数据的方法:数据库 SQLite3.Core Data 的运用: 在本节,将通过对4个文 ...
- IOS数据持久化之归档NSKeyedArchiver
IOS数据持久化的方式分为三种: 属性列表 (自定义的Property List .NSUserDefaults) 归档 (NSKeyedArchiver) 数据库 (SQLite.Core Data ...
- iOS开发中的4种数据持久化方式【一、属性列表与归档解档】
iOS中的永久存储,也就是在关机重新启动设备,或者关闭应用时,不会丢失数据.在实际开发应用时,往往需要持久存储数据的,这样用户才能在对应用进行操作后,再次启动能看到自己更改的结果与痕迹.ios开发中, ...
- iOS数据持久化-OC
沙盒详解 1.IOS沙盒机制 IOS应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒,所以所有的非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文 ...
随机推荐
- hisql 与sqlsugar,freesql 数据插入性能测试
hisql与目前比较流行的ORM框架性能测试对比 hisql 一直定位为新一代的ORM框架 为低代码开发而生 测试数据数据库为sqlserver数据库 测试源码地址hisql与sqlsugar fre ...
- springCloudGateway-使用记录
一.需求描述 旧项目做好之后,已经维护了一两个月,基本上已经趋于稳定,按照项目的整体进度基本上不会在做什么改动.新项目已经确定 下来,只是有一个大概的需求,unity3d的客户端已经开始做,在这个月2 ...
- Apache Ant: If 和 Unless
目录 If And Unless If And Unless 从 Ant 1.9.1 起,可以在所有的任务和嵌套的元素上以特别的命名空间添加 if 和 unless 属性. In order to u ...
- react中使用antd按需加载(第一部)
什么是react按需加载?简单来说就是当我们引用antd的时候需要引入全局css样式,这会对性能造成一定的影响,那么使用按需加载以后就不需要引入css全局样式了,直接引入功能模块即可,既然需要设置按需 ...
- Hive与MapReduce相关排序及自定义UDF函数
原文链接: https://www.toutiao.com/i6770870821809291788/ Hive和mapreduce相关的排序和运行的参数 1.设置每个reduce处理的数据量(单位是 ...
- LINUX学习-Mysql安装
一.安装环境 操作系统CentOS6.8 关闭SeLinux和iptables防火墙 二.网络yum源 将下面的软件下载到 /etc/yum.repos.d/ 的目录下 官方基础:http:// ...
- 从内存管理原理,窥探OS内存管理机制
摘要:本文将从最简单的内存管理原理说起,带大家一起窥探OS的内存管理机制,由此熟悉底层的内存管理机制,写出高效的应用程序. 本文分享自华为云社区<探索OS的内存管理原理>,作者:元闰子 . ...
- 软件开发架构与网络之OSI七层协议(五层)
本期内容概要 python回顾 软件开发架构 网络理论前瞻 osi七层协议(五层) 以太网协议 IP协议 port协议 交换机 路由器 局域网 广域网 TCP协议 三次握手 四次挥手 UDP协议 内容 ...
- sqlmap之--os-shell命令执行原理
最近也是在看sqlmap,感觉--os-shell这个命令确实很厉害,但我并不知道它的原理,所以来研究一下 环境 环境就是我本地搭的一个有sql注入漏洞的一个小demo 演示 这是我们的demo环境 ...
- 马哈鱼血缘分析工具部署介绍--win 10
马哈鱼血缘分析工具部署介绍--win 10 随着大数据技术的发展与普及,数据治理和数据质量变得越来越重要,数据血缘分析在业界悄然兴起并得到了广泛流行,马哈鱼是国内少有的一款专业且易用的血缘分析工具.本 ...