IOS本地存储的四种方式

概要


四种方式:
1. NSKeyedArchiver归档(NSCoding)序列化
2. NSUserDefaults:用来保存应用程序设置和属性、用户保存的数据。
3. NSFileManager write 的方式直接写入磁盘
4. SQLite:采用SQLite数据库来存储数据 

一、NSKeyedArchiver归档(NSCoding)序列化


需要归档解档的类需要遵守NSCoding协议,只有遵守了NSCoding协议的类才可以用NSKeyedArchiver归档和NSKeyedUnarchiver解档。
如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,可以直接用NSKeyedArchiver归档和NSKeyedUnarchiver解档~

缺点:归档的形式来保存数据,只能一次性归档保存以及一次性解压。所以只能
针对小量数据,而且对数据操作比较 笨拙,即如果想改动数据的某一小部分,还
是需要解压整个数据或者归档整个数据。
实现归档的四个步骤
1.遵守NSCoding协议
2.实现encodeWithCoder和initWithCoder方法
3.归档方法
4.解档方法

1.1 遵守NSCoding协议

@interface CreateTrainOnlineCache : NSObject<NSCoding>

1.2 实现encodeWithCoder和initWithCoder方法

- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.name forKey:@"name"];
[coder encodeInteger:self.age forKey:@"age"];
}

- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self) {
    self.age = [coder decodeIntegerForKey:@"age"];
    self.name = [coder decodeObjectForKey:@"name"];
}
return self;
}

1.3 归档

Student *s1 = [[Student alloc] init];
s1.name = @"zzz";
s1.age = 18;

NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
// 这个文件后缀可以是任意的,只要不与常用文件的后缀重复即可,我喜欢用data
NSString *filePath = [path stringByAppendingPathComponent:@"student.data"];
// 归档
[NSKeyedArchiver archiveRootObject:s1 toFile:filePath];

1.4 解档

NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
NSString *filePath = [path stringByAppendingPathComponent:@"student.data"];
// 解档
Student *s = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"%@----%ld", s.name, s.age);

1.5 扩展

1.5.1 用户数据隔离

这里笔者的应用涉及到多用户切换,多用户数据之间相对隔离保密。这里笔者在用户登录app时便以用户登录名为文件名创立了用户目录,读取都在这个个文件目录,不同的用户在沙盒的目录不同,以达到用户数据隔离的效果。

#import "NSFileManager+Paths.h"
-(NSString *)userPath{
    NSString *path = [NSString stringWithFormat:@"%@/%@",[NSFileManager documentsPath],[self account]];
    return path;
}

#pragma mark - 类文件本地归档
-(void)saveCache{
    NSString *userPath = [BSEUserInfo shareMannager].userPath;
    NSString *filePath = [userPath stringByAppendingPathComponent:@"onlineTrain.data"];
    [NSKeyedArchiver archiveRootObject:self toFile:filePath];
}

-(void)deleteCache{
    NSFileManager* fileManager = [NSFileManager defaultManager];
    NSString *userPath = [BSEUserInfo shareMannager].userPath;
    NSString *filePath = [userPath stringByAppendingPathComponent:@"onlineTrain.data"];
    
    BOOL fileExist = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
    if (!fileExist) {
        NSLog(@"not exist");
        return ;
    }else {
        NSLog(@"exist");
        BOOL deleteState = [fileManager removeItemAtPath:filePath error:nil];
        if (deleteState) {
            NSLog(@"delete success");
        }else {
            NSLog(@"delete fail");
        }
        
    }
}
#pragma mark - 加载本地归档
-(BOOL)loadCache{
    NSString *userPath = [BSEUserInfo shareMannager].userPath;
    NSString *filePath = [userPath stringByAppendingPathComponent:@"onlineTrain.data"];
    
    BOOL fileExist = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
    if (!fileExist) {
        NSLog(@"not exist");
        return NO;
    }else {
        CreateTrainOnlineCache *object = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
        share = object;
        return YES;
    }
}

1.5.2 使用runtime动态获取类属性减少冗余代码

#import <objc/runtime.h>

/**
 *  根据类动画获取类的所有属性,不要忘记导入#import <objc/runtime.h>
 *
 *  @param cls <#cls description#>
 *
 *  @return <#return value description#>
 */
- (NSArray *)perperiesWithClass:(Class)cls
{

    NSMutableArray *perperies = [NSMutableArray array];

    unsigned int outCount;
    //动态获取属性
    objc_property_t *properties = class_copyPropertyList(cls, &outCount);

    //遍历person类的所有属性
    for (int i = 0; i < outCount; i++)
    {
        objc_property_t property = properties[i];
        const char *name = property_getName(property);

        NSString *s = [[NSString alloc] initWithUTF8String:name];

        [perperies addObject:s];

    }

    return perperies;
}

/**
 *  归档会触发
 *
 *  @param aCoder <#aCoder description#>
 */
- (void)encodeWithCoder:(NSCoder *)aCoder
{
    for (NSString *perperty in [self perperiesWithClass:[self class]])
    {
        [aCoder encodeObject:perperty forKey:perperty];
    }
}

/**
 *  解归档会触发
 *
 *  @param aDecoder <#aDecoder description#>
 *
 *  @return <#return value description#>
 */
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init])
    {
        for (NSString *perperty in [self perperiesWithClass:[self class]])
        {
            [self setValue:[aDecoder decodeObjectForKey:perperty] forKey:perperty];;
        }

    }

    return self;
}


二、NSUserDefaults 偏好设置


  1. NSUserDefaults 是一个单例对象,在整个应用程序的生命周期中都只有一个实例。
  2. NSUserDefaults保存的数据类型有:
  (1). 基本数据类型(int,NSInter,float,double,CGFlat......)
  (2). NSNumber, NSString, NSData, NSArray, NSDictionary, NSURL。
  1. 其他类要是想适使用NSUserDefaults 存储到本地需要 转换成相应的数据类型才行。如UIImage:
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
UIImage *image=[[UIImage alloc]initWithContentsOfFile:@"photo.jpg"];
NSData *imageData = UIImageJPEGRepresentation(image, 100);//UIImage对象转换成NSData
[defaults setObject: imageData forKey:@"image"];
[defaults synchronize];//立即保存
  1. synchronize。立即保存
    苹果文档这么说:
Writes any modifications to the persistent domains to disk and updates all unmodified persistent domains to what is on disk.
Return Value YES if the data was saved successfully to disk, otherwise NO.

非必要函数,一般写入工作后会触发保存。

应用:

NSUserDefaults一般保存配置信息,比如用户名、密码、是否保存用户名和密码、是否离线下载等一些配置条件信息。
这个相信大家都用过,就不细讲了,直接上方法。

2.1 写入:

//(1)基本数据类型
//保存NSInteger
[defaults setInteger:(NSInteger) forKey:(nonnull NSString *)];
//保存BOOL
[defaults setBool:(BOOL) forKey:(nonnull NSString *)];

//(2)对象
[defaults setObject:@"用户名" forKey:kUsernameKey];

2.1 读取:

//(1)基本数据类型
//保存NSInteger
[defaults setInteger:(NSInteger) forKey:(nonnull NSString *)];
//保存BOOL
[defaults setBool:(BOOL) forKey:(nonnull NSString *)];

//(2)对象
NSString *username = [defaults objectForKey:kUsernameKey];

2.3 删除

//删除指定key的数据
[defaults removeObjectForKey:(nonnull NSString *)];

三、plist文件保存


* plist本身就是XML文件,名字后缀为.plist。
* plist主要保存的数据类型为NSString、NSNumber、NSData、NSArray、NSDictionary。

实现方法:

3.1 写入
/把字典写入到plist文件,比如文件path为:~/Documents/data.plist
[dictionary writeToFile:path atomically:YES];
//把数组写入到plist文件中
[array writeToFile:path atomically:YES];
3.2 读取
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:(nonnull NSString *)]];
NSDictionary *dictionary =  [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:(nullable NSString *) ofType:(nullable NSString *)]];

四、 SQLite数据库


这个有些非常好用的第三方库,很常见就不多讲了直接上连接。
FMDB
这里推荐一个基于FMDB 封装的第三方LKDBHelper,大大简化了数据库的操作。
LKDBHelper-SQLite-ORM
数据库将会在后面再详细总结下,贴出常用的建表 增删查改方法。

五、 CoreData

1 . CoreData提供了一种“对象-关系映射”的功能,能将OC对象转化成数据,保存Sqlite中。
2 . CoreData的好处就是能够合理管理内存,避免sql语句的麻烦(不用写sql语句)。

CoreData构成

  • NSManagedObjectContext:被管理的数据上下文,主要作用:插入、查询、删除。
  • NSManagedObjectModel:数据库所有的表结构和数据结构,包含各个实体的定义的信息。主要作用就是添加实体、实体属性,建立属性之间的关系。
  • NSPersistentStoreCoordinator持久化存储助理对象,相当于数据库的连接器。主要作用就是设置存储的名字、位置、存储方式。
  • NSFetchRequest相当于select语句。查询封装对象。
  • NSEntityDescription实体结构对象,相当于表格结构。
  • 后缀为xxx.xcdatamodeld文件,编译后为xxx.momd的文件。
    创建工程的时候,勾上Use Core Data。如图所示

六、 KeyChain


  • 钥匙串(英文: KeyChain)是苹果公司Mac OS中的密码管理系统。
  • 一个钥匙串可以包含多种类型的数据:密码(包括网站,FTP服务器,SSH帐户,网络共享,无线网络,群组软件,加密磁盘镜像等),私钥,电子证书和加密笔记等。
  • iOS的KeyChain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式。每个iOS程序都有一个独立的KeyChain存储。从iOS 3.0开始,跨程序分享KeyChain变得可行。
  • 当应用程序被删除后,保存到KeyChain里面的数据不会被删除,所以* * KeyChain是保存到沙盒范围以外的地方。
  • KeyChain的所有数据也都是以key-value的形式存储的,这和NSDictionary的存储方式一样。
  • 相比于NSUserDefaults来说,KeyChain保存更为安全,而且KeyChain里面保存的数据不会因为app删除而丢失

iOS 获取沙盒文件路径及 写入/删除 沙盒文件

iOS 本地存储四种方法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容