美文网首页
ios-本地数据存储有哪几种方式?

ios-本地数据存储有哪几种方式?

作者: 不懂技术的工程师 | 来源:发表于2018-12-10 15:42 被阅读0次

    1.NSKeyedArchiver

    在iOS中,对象的序列化和反序列化分别使用NSKeyedArchiver和NSKeyedUnarchiver两个类,我们可以把一个类对象进行序列化(把对象转化成字节流)然后保存到文件中(保存的文件都是加密的),使用时再读取文件,把内容反序列化(把字节流转化成对象)。这个过程通常也被称为对象的编码(归档)和解码(解归档)。
    需要注意的是,NSKeyedArchiver和NSKeyedUnarchiver是继承于NSCoder这个抽象类的,所以我们在归档自定义类的时候需要实现NSCoding协议,并且实现协议方法。
    缺点:只能一次性归档保存以及一次性解压。所以只能针对小量数据,对数据操作比较笨拙,如果想改动数据的某一小部分,需要解压或归档整个数据。
    ---归档Foundation框架的对象

        NSDictionary *infoDiC = @{@"name":@"张三",@"sex":@"男"};
        NSString *filePath = [self getFileName:@"info_dic"];
        //字典归档
        if ([NSKeyedArchiver archiveRootObject:infoDiC toFile:filePath]) {
            NSLog(@"字典归档成功,路径%@",filePath);
        }
        //数组归档
        NSArray *infoArray = @[@"C",@"Swift",@"Python"];
        NSString *filePath_Array = [self getFileName:@"info_array"];
        if ([NSKeyedArchiver archiveRootObject:infoArray toFile:filePath_Array]) {
            NSLog(@"数组归档成功,路径%@",filePath_Array);
        }
        //nsstring 归档
        NSString *infoStr = @"我爱编程";
        NSString *filePath_str = [self getFileName:@"info_str"];
        if ([NSKeyedArchiver archiveRootObject:infoStr toFile:filePath_str]) {
            NSLog(@"字符串归档成功,路径%@",filePath_str);
        }
        //nsnumber 归档
        NSNumber *infoNumber = [NSNumber numberWithInt:100];
        NSString *filePath_Number = [self getFileName:@"info_number"];
        if ([NSKeyedArchiver archiveRootObject:infoNumber toFile:filePath_Number]) {
            NSLog(@"NSNumber归档成功,路径%@",filePath_Number);
        }
        //解归档
        NSDictionary *uninfoDic = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
        NSLog(@"%@",uninfoDic);
    
    - (NSString *)getFileName:(NSString *)name{
    //    因为归档后的文件是加密的,这里的文件后缀可以随便写
        name = [NSString stringWithFormat:@"%@.tfs_archiver",name];
        return [NSString stringWithFormat:@"%@",[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:name]];
    }
    

    ---归档自定义类对象

    //loginModel.h
    #import <Foundation/Foundation.h>
    
    @interface loginModel : NSObject <NSCoding>//实现NSCoding协议
    @property(nonatomic,copy)NSString *userName;
    @property(nonatomic,copy)NSString *nickName;
    @end
    
    //loginModel.m
    #import "loginModel.h"
    
    @implementation loginModel
    //下面是实现NSCoding协议的两个方法
    //归档时调用
    - (void)encodeWithCoder:(NSCoder *)aCoder{
    //    每次归档对象时,都会调用这个方法。一般在这个方法里面指定如何归档对象中的每个实例变量,可以使用encodeObject:forKey:方法归档实例变量
        [aCoder encodeObject:_userName forKey:@"userName"];
        [aCoder encodeObject:_nickName forKey:@"nickName"];
    }
    //解归档时调用
    - (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
    //    每次从文件中恢复(解码)对象时,都会调用这个方法。一般在这个方法里面指定如何解码文件中的数据为对象的实例变量,可以使用decodeObject:forKey方法解码实例变量
        self = [super init];
        if (self) {
            self.userName = [aDecoder decodeObjectForKey:@"userName"];
            self.nickName = [aDecoder decodeObjectForKey:@"nickName"];
        }
        return  self;
    }
    @end
    
    
        loginModel *log = [[loginModel alloc]init];
        log.userName = @"大黄";
        log.nickName = @"点点";
        NSString *filePath_log = [self getFileName:@"info_login"];
        if ([NSKeyedArchiver archiveRootObject:log toFile:filePath_log]) {
            NSLog(@"自定义对象归档成功,路径%@",filePath_log);
        }
        //解归档
        loginModel *unlog = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath_log];
        NSLog(@"%@,%@",unlog.userName,unlog.nickName);
    

    ---对多个对象进行归档
    archiveRootObject: toFile: 方法只能对一个对象进行归档,当我们需要对多个对象进行归档时可以使用如下操作。

        //1.使用NSdata存放归档数据
        NSMutableData *infoData = [NSMutableData data];
        //2.归档文件存储路径
        NSString *filePath_data = [self getFileName:@"info_data"];
        //3.根据data初始化归档对象
        NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:infoData];
        //4.添加归档内容(设置键值对)
        [archiver encodeObject:infoArray forKey:@"Array"];
        [archiver encodeObject:infoDiC forKey:@"Dictionary"];
        [archiver encodeInt:20 forKey:@"age"];
        //5.完成归档
        [archiver finishEncoding];
        //6.存储归档信息
        [infoData writeToFile:filePath_data atomically:YES];
        //解归档
        //1、从磁盘读取文件,生成NSData实例
        NSData *unarchiverData = [NSData dataWithContentsOfFile: filePath_data];
        //2.根据data初始化反归档对象
        NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData: unarchiverData];
        //3.根据key访问值
        NSLog(@"%@",[unarchiver decodeObjectForKey:@"Array"]);
        NSLog(@"%@",[unarchiver decodeObjectForKey:@"Dictionary"]);
        NSLog(@"%d",[unarchiver decodeIntForKey:@"age"]);
        //4.完成解归档
        [unarchiver finishDecoding];
    
    归档成功保存的文件

    2.NSUserDefaults

    以key-value的形式存储在应用包的plist文件中,用来保存应用程序设置和属性、用户保存的数据。用户再次打开程序或关机后这些数据任然存在。NSUserDefaults存储的数据类型包括,NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary。
    注意:

    • NSUserDefaults是一个单例,在整个程序中只有一个实例对象
    • 对相同的key赋值会覆盖上次的值,所以请保证key的唯一性
    • NSUserDefaults存储的对象全是不可变的,就算你存的时候是可变的,最后都会被转成不可变的
    • NSUserDefaults存储的路径是Library/Preferences
    • NSUserDefaults存储的数据是以明文显示的
        //存储对象
        [[NSUserDefaults standardUserDefaults] setObject:@"我爱编程" forKey:@"user_key"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    
        //删除对象
        [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"user_key"];
    
    NSUserDefaults存储的数据

    3.SQLite

    采用SQLite数据库来存储数据,SQLite作为一中小型数据库,应用在iOS中,跟前2种相比,相对比较复杂一些。

    4.CoreData

    CoreData是苹果提供的原生的用于对象化管理数据并且持久化的框架。CoreData本质上是将底层数据库封装成对象进行管理。
    优点:

    • CoreData操作数据不需要使用SQLite代码
    • CoreData把数据用面向对象方式进行管理,操作数据库更方便

    缺点:

    写代码之前要掌握的 Core Data 相关成员对象:

    • 1.NSManagedObject
      被管理的数据记录 Managed Object Model 是描述应用程序的数据模型,这个模型包含 实体(Entity),特性(Property),读取请求(Fetch Request)等
    • 2.NSManagedObjectContext
      管理对象上下⽂文,持久性存储模型对象,参与对数据对象进⾏行各种操作的全过程,并监测 数据对象的变化,以提供对 undo/redo 的支持及更新绑定到数据的 UI。
    • 3.NSPersistentStoreCoordinator
      连接数据库的 Persistent Store Coordinator 相当于数据文件管理器,处理底层的对数据 文件的读取与写入。⼀般我们⽆无需与它打交道。
    • 4.NSManagedObjectModel
      被管理的数据模型,数据结构
    • 5.NSFetchRequest
      数据请求
    • 6.NSEntityDescription
      表格实体结构
    • 7. NSPersistentContainer
      ios10新增的类,封装了应用程序中的CoreData Stack(核心数据栈堆)

    CoreData 手动创建使用流程

    • 1.创建模型文件 [相当于一个数据库]
    • 2.添加实体 [一张表]
    • 3.创建实体类 [相当模型--表结构]
    • 4.生成上下文 关联模型文件生成数据库

    如果在已经创建好的项目里使用core data,此时我们需要手动创建,下面我们手动创建core data并 完成增、删、改、查操作。
    1.创建模型文件

    创建模型文件
    添加实体
    2.添加实体 Student 实体
    3.创建实体类 创建实体类.png
    选择Core Data
    生成的实体类

    4.生成上下文关联模型文件生成数据库
    ios 10以后我们可以直接使用NSPersistentContainer生成。
    AppDelegate.h

    #import <UIKit/UIKit.h>
    #import <CoreData/CoreData.h>
    @interface AppDelegate : UIResponder <UIApplicationDelegate>
    
    @property (strong, nonatomic) UIWindow *window;
    @property (nonatomic,strong)NSPersistentContainer *persistentContainer;//CoreData Stack容器
    //保存上下文
    - (void)saveContext;
    
    @end
    

    AppDelegate.m

    - (NSPersistentContainer *)persistentContainer{
        if (!_persistentContainer) {
            _persistentContainer = [[NSPersistentContainer alloc]initWithName:@"CoreData"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                }
            }];
        }
        return _persistentContainer;
    }
    - (void)saveContext {
        NSManagedObjectContext *context = self.persistentContainer.viewContext;
        NSError *error = nil;
        if ([context hasChanges] && ![context save:&error]) {
            NSLog(@"Unresolved error %@, %@", error, error.userInfo);
        }
    }
    

    插入数据

    - (void)insertCoreData{
        //创建一个实体 Entity 相当于数据库中的一个表,它描述一种抽象数据类型,其对应的类为 NSManagedObject 或其子类
        Student *student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.persistentContainer.viewContext];
        student.name = @"ZhangSan";
        student.age = 28;
        //保存上下文
        [self saveContext];
    }
    

    在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中调用 insertCoreData

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        [self insertCoreData];
        return YES;
    }
    

    运行后在项目的沙盒目录下生成了如下文件:

    生成的CoreData.sqlite文件

    使用终端查看CoreData.sqlite(也可以使用其他sqlite客户端打开)

    未命名.png
    删除数据
    - (void)deleteCoreData{
        //获取数据请求对象,指明实体进行删除操作
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
        //通过创建谓词对象,然后过滤掉符合要求的对象,也就是要删除的对象
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@",@"ZhangSan"];
        request.predicate = predicate;
        //获取所有实体对象
        NSError *error = nil;
        NSArray<Student *> *students = [self.persistentContainer.viewContext executeFetchRequest:request error:&error];
        if (!error) {
            //遍历对象 找到符合要求的对象 删除
            [students enumerateObjectsUsingBlock:^(Student * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                [self.persistentContainer.viewContext deleteObject:obj];
            }];
        }else{
            NSLog(@"Error:%@",error);
        }
        //保存上下文
        [self saveContext];
    }
    

    修改数据

    - (void)updateCoreData{
        //获取数据请求对象,指明实体进行修改操作
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
        //创建谓词对象,设置过滤条件
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age like[cd] %@",@"28"];
        request.predicate = predicate;
        NSError *error = nil;
        NSArray <Student*> *students = [self.persistentContainer.viewContext executeFetchRequest:request error:&error];
        if (!error) {
            [students enumerateObjectsUsingBlock:^(Student * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                obj.name = @"王大锤";
            }];
        }else{
            NSLog(@"Error:%@",error);
        }
        //保存上下文
        [self saveContext];
    }
    

    查询数据

    - (void)checkCoreData{
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
        NSError *error = nil;
        NSArray <Student*> *students = [self.persistentContainer.viewContext executeFetchRequest:request error:&error];
        if (!error) {
            [students enumerateObjectsUsingBlock:^(Student * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                NSLog(@"%@",obj.name);
                NSLog(@"%d",obj.age);
            }];
        }else{
            NSLog(@"Error:%@",error);
        }
    }
    

    ps:
    在创建实体类时如果遇到 duplicate symbol错误问题,
    1.选中你的数据模型文件xcdatamodeld的某个实体
    2.然后在XCode右侧栏中切换Data Model Inspector(第三栏)
    3.在Codegen一栏中将Class Definition换成Manual/None,这里表示不自动生成实体类型定义 ,然后重新编译。
    关于谓词的使用苹果的官方文档在这里
    关于使用终端查看sqlite请看这里

    好了ios本地数据存储介绍完了。以上内容是本人自己实践总结 如有错误请及时批评指正,谢谢!

    core data详细的教程可以去这里看看

    相关文章

      网友评论

          本文标题:ios-本地数据存储有哪几种方式?

          本文链接:https://www.haomeiwen.com/subject/rwtdhqtx.html