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

2018-12-10  本文已影响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 standardUserDefaults] setObject:@"我爱编程" forKey:@"user_key"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    //删除对象
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"user_key"];
NSUserDefaults存储的数据

3.SQLite

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

4.CoreData

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

缺点:

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

CoreData 手动创建使用流程

如果在已经创建好的项目里使用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详细的教程可以去这里看看

上一篇下一篇

猜你喜欢

热点阅读