iOS 数据持久化

2019-11-29  本文已影响0人  Ray0218

iOS常见的数据持久化方式主要有以下几点:

plist存储

通常叫做plist文件,用于存储在程序中不经常修改、数据量小的数据,不支持自定义对象存储,支持数据存储的类型为:Array,Dictionary,String,Number,Data,Date,Boolean,通常用来存放接口名、城市名、银行名称、表情名等极少修改的数据
直接创建.plist文件, 如果文件类型是Array就用数组获取,如果是dictionary就用字典获取

//获取plist文件路径
    NSString *path = [[NSBundle mainBundle] pathForResource:@"testProperty" ofType:@"plist"];
    NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
   // NSArray *array = [NSArray arrayWithContentsOfFile:path];

偏好设置(NSUserDefaults)

用于存储用户的偏好设置,同样适合于存储轻量级的用户数据,数据会自动保存在沙盒的Libarary/Preferences目录下,本质上就是一个plist文件,所以同样的不支持自定义对象存储,支持数据存储的类型为:Array,Dictionary,String,Number,Data,Date,Boolean,可以用做检查版本是否更新、是否启动引导页、自动登录、版本号等等,需要注意的是NSUserDefaults是定时的将缓存中的数据写入磁盘,并不是即时写入,为了防止在写完NSUserDefaults后,程序退出导致数据的丢失,可以在写入数据后使用synchronize强制立即将数据写入磁盘

//存
[[NSUserDefaults standardUserDefaults] setObject:phoneNumber forKey:@"key"];
  [[NSUserDefaults standardUserDefaults] synchronize];
//取
[[NSUserDefaults standardUserDefaults] objectForKey:@"key"];

文件写入(writeToFile)

一 、沙盒路径

沙盒主路径:是程序运行期间系统会生成一个专属的沙盒路径,应用程序在使用期间非代码的文件都存储在当前的文件夹路径里面
Documents:用来存储永久性的数据的文件 程序运行时所需要的必要的文件都存储在这里(数据库)iTunes会自动备份这里面的文件
Library:用于保存程序运行期间生成的文件
Caches:文件夹位于Library中用于保存程序运行期间产生的缓存文件
Preferences:文件夹位于Library中,主要是保存一些用户偏好设置的信息,一般情况下,我们不直接打开这个文件夹 而是通过NSUserDefaults进行偏好设置的存储
tmp:临时文件夹---程序运行期间产生的临时岁骗会保存在这个文件夹中 通常文件下载完之后或者程序退出的灰自动清空此文件夹iTunes不会备份这里的数据。

//获取沙盒路径
 NSString *homePath =NSHomeDirectory();
    NSLog(@"%@",homePath);

    //第一个参数:要查询的文件的路径
    //第二个参数:要查询路径所属的用户 iOS是单用户
    //第三个参数的意思 YES是绝对路径 NO是相对路径
    //区别于OS-X系统 iOS应用文件夹中通常只有一个文件路径 由于OC同时支持的苹果系列产品的开发 在MacOS里面会同时存在很多软件 通常生成的路径放在一个数组里面
    //iOS端一次只有一个应用 所以取数组唯一的一个元素即可
    NSArray *documentArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO);
    NSLog(@"%@",documentArray);//打印的结果是 "~/Documents"
    NSString *documentPath = [documentArray firstObject];
    NSLog(@"documentPath = %@",documentPath);//结果是 ~/Documents

   对比以上我们可以打印试着获取几个路径
    NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)firstObject];
    NSLog(@"libraryPath = %@",libraryPath);
    NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)firstObject];
    NSLog(@"ChchesPath = %@",cachesPath);
    NSString *preferencePath =[libraryPath stringByAppendingString:@"/Preferences"];
    NSLog(@"%@",preferencePath);

二、简单对象写入文件

文件的写入会替换调原有文件的内容

-(void)writeToFiled:(NSString*)appendPath data:(id)data{
   
   
   //第一个参数:要查询的文件的路径
   //第二个参数:要查询路径所属的用户 iOS是单用户
   //第三个参数的意思 YES是绝对路径 NO是相对路径
   //区别于OS-X系统 iOS应用文件夹中通常只有一个文件路径 由于OC同时支持的苹果系列产品的开发 在MacOS里面会同时存在很多软件 通常生成的路径放在一个数组里面
   //iOS端一次只有一个应用 所以取数组唯一的一个元素即可
   //writetofile 保存的数组和字典是以plist格式书写的
   NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask ,YES).firstObject;
   
   NSString *filePath = [documentPath stringByAppendingString:appendPath];
   
   if (![[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
       [[NSFileManager defaultManager]createFileAtPath:filePath contents:nil attributes:nil] ;
   }
   
   NSError *error ;
   BOOL isSuccess =   NO;
   
   if ([data isKindOfClass:[NSString class]]) {
       isSuccess =   [data writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
//    取
   //    NSString *contentString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

   }else if([data isKindOfClass:[NSArray class]] || [data isKindOfClass:[NSDictionary class]]  || [data isKindOfClass:[NSData class]]){
       isSuccess = [data writeToFile:filePath atomically:YES];
//    取
   //    NSArray *readArray = [NSArray arrayWithContentsOfFile:path];
// NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:path];
// NSData *data = [NSData dataWithContentsOfFile:path];
   }
   
   if (isSuccess && error == nil) {
       NSLog(@"成功") ;
   }
   else{
       
       NSLog(@"error == %@",error) ;
   }
   
}

如果需要将数据持续写入,通过NSFileHandle进行操作

-(void)writeAddToFiled:(NSString*)appendPath data:(id)data{
  
    NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask ,YES).firstObject;
    
    NSString *filePath = [documentPath stringByAppendingString:appendPath];
    
    if (![[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
        [[NSFileManager defaultManager]createFileAtPath:filePath contents:nil attributes:nil] ;
    }
    NSData *rData = nil ;
    if ([data isKindOfClass:[NSData class]]) {
        rData = data ;
    }else  if ([data isKindOfClass:[NSArray class]] || [data isKindOfClass:[NSDictionary class]]) {
        rData = [NSJSONSerialization dataWithJSONObject:data options:NSJSONWritingPrettyPrinted error:nil];
        
    }else if ([data isKindOfClass:[NSString class]]){
        rData = [data dataUsingEncoding:NSUTF8StringEncoding] ;
    }
    
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath] ;
    if (fileHandle) {
        [fileHandle seekToEndOfFile];
        [fileHandle writeData:rData] ;
        [fileHandle closeFile];
    }
    
}

二、复杂对象写入文件

在这里有一点是需要我们区分的,归档并不是数据持久化的方式 而是辅助复杂对象转化成简单对象的一种方式 其实真正实现数据持久化的仍然是写入文件writeToFile,归档后的文件是加密的,也更加的安全

创建Model类继承自NSObject ,遵守NSCoding或者NSSecureCoding协议,重写两个方法。 如果采用NSSecureCoding协议,必须重写supportsSecureCoding 方法并返回YES

@interface KLUserModel : NSObject<NSSecureCoding>
@property(nonatomic,strong)NSString *rName;
@property(nonatomic,assign)int rAge;
@end

@implementation KLUserModel


- (void)encodeWithCoder:(nonnull NSCoder *)coder {
    NSLog(@"encodeWithCoder");
    [coder encodeObject:_rName forKey:@"name"];
    [coder encodeInt:_rAge forKey:@"age"];
}

- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
    if (self = [super init]) {
                NSLog(@"initWithCoder");
        self.rName = [coder decodeObjectForKey:@"name"];
        self.rAge = [coder decodeIntForKey:@"age"];

    }
    return self ;
}
+(BOOL)supportsSecureCoding{
    
    return YES ;
}

@end

//取数据
-(void)pvt_getModel{
    //会调用对象的initWithCoder方法
    NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES).firstObject ;
    
    NSData *data = [NSData dataWithContentsOfFile:[documentPath stringByAppendingString:@"/user.text"]] ;
    
    if (@available(iOS 11.0, *)) {
        NSError *error ;
        
        id content = [NSKeyedUnarchiver unarchivedObjectOfClass:[KLUserModel class] fromData:data error:&error];
        
    } else {
        //Fallback on earlier versions
        
        //       id content = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        id content = [NSKeyedUnarchiver unarchiveObjectWithFile:[documentPath stringByAppendingString:@"/user.text"]];
        
    }
    
}
//存数据
-(void)pvt_saveModel {
        //会调用对象的encodeWithCoder方法
    KLUserModel *model = [KLUserModel new] ;
    model.rName = @"the name";
    model.rAge = 19;
    
    NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES).firstObject ;
    
    if (@available(iOS 11.0, *)) {
        NSError *error ;
        
        NSData *data = [NSKeyedArchiver  archivedDataWithRootObject:model requiringSecureCoding:YES error:&error] ;
        
        if (error) {
            return;
        }
        
        [data writeToFile:[documentPath stringByAppendingString:@"/user.text"] atomically:YES];
        
    }else{
        
        BOOL isSuccess = [NSKeyedArchiver archiveRootObject:model toFile:[documentPath stringByAppendingString:@"/user.text"]];
        if (isSuccess) {
            NSLog(@"Success") ;
        }
        
    }
    
    
}

CoreData 数据存储

1. 导入 CoreData 框架:

Build Phases -> Link Binary With Libraries -> 添加 CoreData.framework

2. 创建xcdatamodeld 文件

File -> New -> File -> iOS -> Core Data -> Data Model -> XXX.xcdatamodeld

3. 创建实体

XXX.xcdatamodeld -> Add Entity -> 添加对应的 Attributes
创建好实体对象XXX.xcdatamodeld之后,右侧属性栏Code Generation下面的Language默认为Swift,这里使用OC,就改成Objective-C;

//格式
实体名(表名)+CoreDataClass.h
实体名(表名)+CoreDataClass.m
实体名(表名)+CoreDataProperties.h
实体名(表名)+CoreDataProperties.m
//例如,实体名(表名)为Userinfo,对应的关联文件为:
Userinfo+CoreDataClass.h
Userinfo+CoreDataClass.m
Userinfo+CoreDataProperties.h
Userinfo+CoreDataProperties.m
Userinfo+CoreDataClass.h
Userinfo+CoreDataClass.m
Userinfo+CoreDataProperties.h
Userinfo+CoreDataProperties.m
4. 创建数据库
/**
 管理数据的对象
 */
@property (nonatomic, strong) NSManagedObjectContext *rContext;

-(void)pvt_createDB{
    
    NSURL *pathURL = [[NSBundle mainBundle]URLForResource:@"KLCoreModel" withExtension:@"momd"];;
    NSManagedObjectModel *mode = [[NSManagedObjectModel alloc]initWithContentsOfURL:pathURL]; ;
    
     NSPersistentStoreCoordinator *coord = [[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:mode];
    
    NSString *dpPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingString:@"/userInfo.sqlite"];
    NSURL *url = [NSURL fileURLWithPath:dpPath];
    NSError *error;
    [coord addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
    
    if (error == nil) {
        NSLog(@"数据库添加成功");
    } else {
        NSLog(@"数据库添加失败%@",error);
    }
    
    _rContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSMainQueueConcurrencyType] ;
    _rContext.persistentStoreCoordinator = coord ;
}
5. 插入数据
-(void)pvt_addData{
    Userinfo *rperson = [NSEntityDescription insertNewObjectForEntityForName:@"Userinfo" inManagedObjectContext:self.rContext];
    rperson.rName = @"ray";
    rperson.rAge = 18;
    
   [self.rContext save:nil];
 }
6. 删除数据,修改数据,查询数据
-(void)pvt_changeData{
    
    NSFetchRequest * fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Userinfo"] ;
    
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"rAge=%d",18] ;
    fetchRequest.predicate = predicate ;
    NSError *error;
//查询数据,如不设置predicate 返回所有数据
    NSArray *results = [self.rContext executeFetchRequest:fetchRequest error:&error] ;
    if (results) {
        for (Userinfo *user in results) {
//修改数据
            user.rAge = 11 ;
//删除数据
          //  [_context deleteObject:user];
        }
        
     [self.rContext save:nil];
     }
    
}

参考:https://www.jianshu.com/p/5b13960eefa2
https://www.jianshu.com/p/e41e73f4edec
https://www.jianshu.com/p/e98daf9fec78
https://www.jianshu.com/p/1cf3de935d21

上一篇 下一篇

猜你喜欢

热点阅读