将来跳槽用iOS接下来要研究的知识点程序员

05-iOS数据存储

2020-10-11  本文已影响0人  片片碎

一、iOS沙盒机制

二、应用沙盒

2.1、沙盒路径下的三个文件

沙河路径.png

2.2、获取沙盒路径的两种方式

2.3、沙盒文件操作-NSFileManager和NSFileHandle

3.4.1、 NSFileManage
 NSFileManager *fm = [NSFileManager defaultManager];
    NSString *createDirPath = @"/Users/Feng/Desktop/aaa/ccc/bbb/love.txt";
 
    // 创建路径的时候,YES自动创建路径中缺少的目录,NO的不会创建缺少的目录; 
    //attributes:属性的字典;
    //error:错误对象
    NSError *error = nil;
   NSString *path = [NSString stringWithFormat:@"%@/test",[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]];
    BOOL isYES = [fm createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
    if (isYES) {
        NSLog(@"成功");
   }
    NSData *data = [@"hello word,my name is Feng" dataUsingEncoding:NSUTF8StringEncoding];//数据
    BOOL isYes;
   NSString *path = [NSString stringWithFormat:@"%@/test/log.txt",  [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]];
    isYes = [fm createFileAtPath:createDirPath contents:data attributes:nil];
    NSLog(@"isYes = %d",isYes);
    NSString *targetPath = @"/Users/Feng/Desktop/aaa/ccc/love.txt";
    NSString *sourcePath = @"/Users/Feng/Desktop/aaa/love.txt";
    [fm removeItemAtPath:targetPath error:nil];
    NSString *targetPath = @"/Users/Feng/Desktop/aaa/ccc/love.txt";
    NSString *sourcePath = @"/Users/Feng/Desktop/aaa/love.txt";
    [fm copyItemAtPath:sourcePath toPath:targetPath error:nil];
    NSString *targetPath = @"/Users/Feng/Desktop/aaa/ccc/love.txt";
    NSString *sourcePath = @"/Users/Feng/Desktop/aaa/love.txt";
    [fm moveItemAtPath:sourcePath toPath:targetPath error:nil];
// 1、判断文件是否存在
[fm fileExistsAtPath:filePath]; // YES:存在 

//2、判断是否是一个目录 
 [fm fileExistsAtPath:filePath isDirectory:&isDir];YES:是一个目录【首先要判断文件是否存在在判断是否是个目录】

//3、判断文件是否可读
[fm isReadableFileAtPath:filePath];//YES :可读

//4、判断文件是否可写
[fm isWritableFileAtPath:filePath];//YES :可写
    //1、如何获取文件的信息(属性)
    NSDictionary *dict = [fm attributesOfItemAtPath:@""/Users/Feng/Desktop/arr.plist""error:nil];

   //2、获取指定目录下文件及子目录
    //方法一、使用递归的方式 获取当前目录及子目录下的所有的文件及文件夹
    NSArray *subPaths = [fm subpathsAtPath:dirPath];
    
    //方法二、(推荐使用)subpathsOfDirectoryAtPath 不是使用递归的方式获取的
    subPaths = [fm subpathsOfDirectoryAtPath:dirPath error:nil];  
    NSLog(@"subPaths = %@",subPaths);

   //3、获取指定目录下的文件及目录信息(不在获取后代路径)
    subPaths = [fm contentsOfDirectoryAtPath:dirPath error:nil];
3.4.2、 NSFileHandle-读写文件操作
 //光标开始位置到结尾,默认光标在文件最开始
  NSLog(@"%@",[fileHandle readDataToEndOfFile]);
 //部分数据
  //[fileHandle seekToFileOffset:0];  //移动光标到最开始位置,0为最开始
  //NSLog(@"%@",[fileHandle readDataOfLength:3]);

三、iOS常见的五种存储方式

3.1、XML 属性列表plist文件存储

实际应用,如下代码:

NSDictionary *dict = @{
                           @"name":@"zhangsan",
                           @"age" : @(18),
                           };

//获取documenth路径
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

//在document路径下创建一个test.plist文件
NSString *filePath = [documentPath stringByAppendingPathComponent:@"test.plist"];//  // 拼接一个文件名:自动加一个斜杠,拼接文件专用
//写入数据
[dict writeToFile:filePath atomically:YES];

查看沙盒文件,如下图:

image

读取刚存储的plist数据:

 //获取documenth路径
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

//在document路径下创建一个test.plist文件
NSString *filePath = [documentPath stringByAppendingPathComponent:@"test.plist"];
//读取test.plist数据
NSDictionary *listDict = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSLog(@"listDict -- %@",listDict);

对其参数进行解读:

3.2、NSUserDefaults的Preference偏好设置

特点:

优点:

缺点:

示范:

#pragma mark --写数据
- (void)writeData {
   NSArray *testArray = @[@"test1", @"test2", @"test3"];
   [[NSUserDefaults standardUserDefaults] setObject:testArray forKey:@"arrayKey"];
   [[NSUserDefaults standardUserDefaults] synchronize];

   NSDictionary *dic = [NSDictionary dictionaryWithObject:@"Feng" forKey:@"name"];
   [[NSUserDefaults standardUserDefaults] setObject:dic forKey:@"dicKey"];
   [[NSUserDefaults standardUserDefaults] synchronize];

   [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"boolKey"];
   [[NSUserDefaults standardUserDefaults] synchronize];

   [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:88] forKey:@"numberKey"];
   [[NSUserDefaults standardUserDefaults] synchronize];

   [[NSUserDefaults standardUserDefaults] setObject:@"hello world" forKey:@"strKey"];
   [[NSUserDefaults standardUserDefaults] synchronize];

   [[NSUserDefaults standardUserDefaults] setObject:[NSData dataWithData:[@"I am fine" dataUsingEncoding:NSUTF8StringEncoding]] forKey:@"dataKey"];
   [[NSUserDefaults standardUserDefaults] synchronize];

   [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:@"DateKey"];
   [[NSUserDefaults standardUserDefaults] synchronize];
}
#pragma mark --读数据数据
- (void)readData {
   NSArray *testArray = [[NSUserDefaults standardUserDefaults] objectForKey:@"arrayKey"];
   NSLog(@"data:%@",testArray);
}
image.png

3.3、NSKeyedArchiver归档存储

归档一般都是保存自定义对象的时候,使用归档.因为plist文件不能够保存自定义对象.
plist 与 NSUserDefaults(个人偏好设置)两种类型的储存只适用于系统自带的一些常用类型,而且前者必须拿到文件路径,后者也只能储存应用的主要信息。
如果一个字典当中保存有自定义对象,如果把这个字典写入到文件当中,它是不会生成plist文件的。
所以如果有自定义对象的时候,就需要归档了,归档一般都是保存自定义对象的。

但是此方法只能存储一个,要存储多个对象要采用如下的方法:

- (void)archiveManyObject:(id)sender {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentFilePath = paths.firstObject  ;
    NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"];
  
    NSMutableData *data = [[NSMutableData alloc] init];
    NSKeyedArchiver *archiver =  [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; //将数据区连接到NSKeyedArchiver对象
  
    Person *p1 = [[Person alloc] init];
    p1.name = @"ran1";
    p1.age = @"18";
    [archiver encodeObject:p1 forKey:@"person1"];
  
    Person *p2 = [[Person alloc] init];
    p2.name = @"ran2";
    p2.age = @"19";
    [archiver encodeObject:p2 forKey:@"person2"];
    [archiver finishEncoding];
    [data writeToFile:filePath atomically:YES];
}

- (void)unarchiveManyObject:(id)sender {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentFilePath = paths.firstObject  ;
    NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"];
    NSData *data = [NSData dataWithContentsOfFile:filePath];
  
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    Person *p1 =  [unarchiver decodeObjectForKey:@"person1"];
    Person *p2 =  [unarchiver decodeObjectForKey:@"person2"];
    [unarchiver finishDecoding];
    NSLog(@"name:%@ age:%@", p1.name, p1.age);
}

3.4、系统提供的数据存储方式的弊端

1、一次性存储,不方便操作大量的数据

3.5、数据库存储

以上的三种存储,需要对文件/文件夹操作都是通过NSFileManager来实现,而数据的存取必须是一次性的全部操作,所以在频繁操作数据方面性能有所欠缺。利用数据库可以解决这个问题。

3.5.1、数据存储使用的SQLite:

3.5.2、FMDB

3.5.2.1、FMDB使用

使用FMDB的时候首先将FMDB使用cocopods导入到我们的工程

pod 'FMDB'

之后在我们的工程中导入需要的头文件即可:

#import "FMDatabase.h"

开发中我们一般会把数据存储到沙河路径下:

NSString * docPath = 
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];//获取Document沙河地址
NSString * dbName = @"myDataBase.db" //数据库名称
NSString * fileName=[docPath stringByAppendingPathComponent: dbName];
FMDatabase * fmdb=[FMDatabase databaseWithPath:fileName];//创建并获取数据库信息
if ([fmdb open]) {
  NSLog(@"数据库打开成功!");
}else{
   NSLog(@"数据库打开失败!");
}
BOOL ret = [m_fmdb open]; //.在数据库中进行增删改查操作时,需要判断数据库是否open,如果open失败,可能是权限或者资源不足
if (ret) {
   NSString * tableName = @"myTable" //数据表名称
  //create table if not exists myTable:如果不存在myTable,则创建;
  //数据表字段
  //'id' INTEGER PRIMARY KEY AUTOINCREMENT:数据类型为integer,并且为基建自增;【键要想实现自动增长,不能是text类型】
  // name:数据类型为字符串text,默认值为NULL(空)
  //age:数据类型为整形integer,默认值为NULL(空)
  //sex:数据类型为字符串text,默认值为NULL(空)
  NSString *sql = [NSString stringWithFormat:@"create table if not exists %@  ('id' INTEGER PRIMARY KEY AUTOINCREMENT,'name' TEXT NOT NULL,'age' integer NOT NULL,'sex' TEXT NOT NULL)", tableName];
  ret = [fmdb executeUpdate: sql];
  if (ret) {
    NSLog(@"创建表成功");
  } ret {
    NSLog(@"创建表失败");
  }
}
/**
 //1.sql语句中跟columnname 绑定的value 用 ?表示,不加‘’,可选参数是对象类型如:NSString,不是基本数据结构类型如:int,方法自动匹配对象类型
 - (BOOL)executeUpdate:(NSString*)sql, ...;
 //2.sql语句中跟columnname 绑定的value 用%@或%d表示,不加‘’
 - (BOOL)executeUpdateWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
 //3.sql语句中跟columnname 绑定的value 用 ?表示的地方依次用 (NSArray *)arguments 对应的数据替代
 - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments;
 //4.同3 ,区别在于多一个error指针,记录更新失败
 - (BOOL)executeUpdate:(NSString*)sql values:(NSArray * _Nullable)values error:(NSError * _Nullable __autoreleasing *)error;
 //5.同3,区别在于用 ? 表示的地方依次用(NSDictionary *)arguments中对应的数据替代
 - (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments;
 - (BOOL)executeUpdate:(NSString*)sql withVAList: (va_list)args;
 */

-(BOOL)executeInsertWithTableName:(NSString *) tableName person::(FengPeron *)person{ 
     //当操作数据库的时候,要先打开数据库,使用完之后关闭
    NSString * tableName = @"myTable" //数据表名称
    NSString *sql = [NSString stringWithFormat:@"insert into '%@' (name,age,sex) values(:name,:age,:sex)",myTable];
    return [m_fmdb executeUpdate:sql withParameterDictionary:[person yy_modelToJSONObject]]; //字典方式
}

-(BOOL)executeInsertWithTableName:(NSString *) tableName name:(NString *)name age:(NSInteger)age  sex:(NString *)sex{ 
   NSString *sql = [NSString stringWithFormat:@"insert into '%@' (name,age,sex) values('%@',%zd,'%@')",myTable,name,age,sex];
   return [m_fmdb executeUpdate:sql]; 
}

-(BOOL)executeDeleteWithTableName:(NSString *) tableName name:(NSString *)name {
    NSString *sql = [NSString stringWithFormat:@"delete from %@ where name = '%@'", tableName, name]; //删除name为传入的值 这行
    return [m_fmdb executeUpdate:sql];
}
-(BOOL)executeUpdateWithTableName:(NSString *) tableName name:(NSString *)name sex:(NSString *)sex{
   NSString *sql = [NSString stringWithFormat:@"update %@ set age = %d where name = '%@'", tableName,age,name];
   return [m_fmdb executeUpdate:sql];
}
-(void)executeQueryWithTableName:(NSString *) tableName {
//错误的拼接和执行模糊查询的方式:
FMResultSet *resultSet = [_db executeQueryWithFormat:@"select * from %@ where name like '%%%@%%'",tableName,keyWord];

// 正确的拼接和执行模糊查询的方式:
NSString *sql = [NSString stringWithFormat:@"select * from %@  where name like '%%%@%%'",tableName,keyWord];

FMResultSet *resultSet = [m_fmdb executeQuery:sql];

/**
FMResultSet根据column name获取对应数据的方法
intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumnIndex:
objectForColumn:
*/

-(NSMutableDictionary *)executeQueryWithTableName:(NSString *) tableName {
    NSMutableDictionary *mutalDic = [[NSMutableDictionary alloc] init];
    NSString *sql = [NSString stringWithFormat:@"select * from %@",tabbleName]; //查询整个表
    FMResultSet * resultSet = [m_fmdb executeQuery:sql];
    if([resultSet next]) { //如果为真
          FengPerson *person = [FengPerson yy_modelWithDictionary:[resultSet resultDictionary]];
          [mutalDic setValue: person forKey: person.name];
     }
     [resultSet close];
     return mutalDic;
}

【使用】第四步删除数据表Table-executeUpdate

-(BOOL)executeDeleteWithTableName:(NSString *) tableName {
   NSString *sql = [NSString stringWithFormat:@"drop table if exists %@",tabbleName]; //如果表格存在 则销毁
   BOOL ret = [fmdb executeUpdate:sql];
    return ret;
}

【使用】第五步关闭数据库DB

-(BOOL)closeDbFile{
    BOOL ret = YES;
    if (fmdb) {
        ret= [fmdb close];
    }
    fmdb = nil;
    return ret;
}
3.5.2.2、FMDB的事务

事务(Transaction)是兵法操作的基本单位,是指单个逻辑工作单位执行的一系列操作序列,这些操作要不都成功,要不不就都不成功,事务是数据库维护数据一致性的单位,在每个事物结束时,都能保持数据一致性与准确性,通常事务跟程序是两个不同的概念,一个程序中包含多个事务,事务主要解决并发条件下操作数据库,保证数据一致性。

1.fmdb 封装transaction 方法,操作简单

未使用事务,一系列操作

-(void)executeInsertWithDevices:(NSArray <ArgDevice *> *)devices {
     NSString *sql = [NSString stringWithFormat:@"insert into '%@' (deviceId,mac,image,name,productKey,houseId,roomId,nodeType) values(:deviceId,:mac,:image,:name,:productKey,:houseId,:roomId,:nodeType)",FMDB_House_Table_NAME(m_houseId)];
     NSDate *begin = [NSDate date];
     for (ArgDevice *device in devices) {
         [m_fmdb executeUpdate:sql withParameterDictionary:[device yy_modelToJSONObject]];
      }
      NSDate *end = [NSDate date];
      NSTimeInterval time = [end timeIntervalSinceDate:begin];
      NSLog(@"不在事务中执行插入任务 所需要的时间 = %f",time);
  }

学习资料:https://www.jianshu.com/p/7958d31c2a97

3.5.3、CoreData

CoreData 是苹果给出的一套基于 SQLite 的数据存储方案.

由于直接使用苹果提供的CoreData容易出错,这里提供一个很好的三方库 MagicalRecord 。
学习资料:https://www.jianshu.com/p/5818f70a37cf

三、总结

接口数据缓存存储方式:

当然对于一些涉及查询、删除、更新等操作的数据模型,就需要使用数据库操作。

上一篇 下一篇

猜你喜欢

热点阅读