zoukankan      html  css  js  c++  java
  • 缓存

    应用沙盒

    • 每个iOS应用都有自己的应用沙盒(应用沙盒就是文件系统目录),与其他文件系统隔离。应用必须待在自己的沙盒里,其他应用不能访问该沙盒
    • 应用沙盒的文件系统目录,如下图所示(假设应用的名称叫Layer)

      应用沙盒的文件系统目录
    • 应用沙盒结构分析

      • 应用程序包:(上图中的Layer)包含了所有的资源文件和可执行文件
      • Documents:保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录

      • tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录

      • Library/Caches:保存应用运行时生成的需要持久化的数据,iTunes同步设备时不会备份该目录。一般存储体积大、不需要备份的非重要数据

      • Library/Preference:保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录

    • 应用沙盒目录的常见获取方式

      • 沙盒根目录:NSString *home = NSHomeDirectory();

      • Documents:(2种方式)

        • 利用沙盒根目录拼接”Documents”字符串

          NSString *home = NSHomeDirectory();
          NSString *documents = [home stringByAppendingPathComponent:@"Documents"];
          // 不建议采用,因为新版本的操作系统可能会修改目录名
        • 利用NSSearchPathForDirectoriesInDomains函数

          // NSUserDomainMask 代表从用户文件夹下找
          // YES 代表展开路径中的波浪字符“~”
          NSArray *array =  NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
          // 在iOS中,只有一个目录跟传入的参数匹配,所以这个集合里面只有一个元素
          NSString *documents = [array objectAtIndex:0];
      • tmp:NSString *tmp = NSTemporaryDirectory();

      • Library/Caches:(跟Documents类似的2种方法)

        • 利用沙盒根目录拼接”Caches”字符串
        • 利用NSSearchPathForDirectoriesInDomains函数(将函数的第2个参数改为:NSCachesDirectory即可)
      • Library/Preference:通过NSUserDefaults类存取该目录下的设置信息


    iOS应用数据存储的常用方式

    1. XML属性列表(plist)归档
    2. Preference(偏好设置)
    3. NSKeyedArchiver归档(NSCoding)
    4. SQLite3
    5. Core Data

    XML属性列表(plist)归档

    • 属性列表是一种XML格式的文件,拓展名为plist

    • 如果对象是NSStringNSDictionaryNSArrayNSDataNSNumber等类型,就可以使用writeToFile:atomically:方法直接将对象写到属性列表文件中

    • 举个例子

      • 将一个NSDictionary对象归档到一个plist属性列表中

        // 将数据封装成字典
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        [dict setObject:@"母鸡" forKey:@"name"];
        [dict setObject:@"15013141314" forKey:@"phone"];
        [dict setObject:@"27" forKey:@"age"];
        // 将字典持久化到Documents/stu.plist文件中
        [dict writeToFile:path atomically:YES];

        成功写入到Documents目录下:


        运行结果


        用文本编辑器打开,文件内容为:


        文本编辑器查看


        用xcode打开属性文件:


        xcode查看
      • 读取属性列表,恢复NSDictionary对象

        // 读取Documents/stu.plist的内容,实例化NSDictionary
        NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
        NSLog(@"name:%@", [dict objectForKey:@"name"]);
        NSLog(@"phone:%@", [dict objectForKey:@"phone"]);
        NSLog(@"age:%@", [dict objectForKey:@"age"]);

        输出结果
    • 属性列表-NSDictionary的存储和读取过程


      属性列表-NSDictionary的存储和读取过程

      Preference(偏好设置)

    • 很多iOS应用都支持偏好设置,比如保存用户名、密码、字体大小等设置,iOS提供了一套标准的解决方案来为应用加入偏好设置功能

    • 每个应用都有个NSUserDefaults实例,通过它来存取偏好设置

    • 比如,保存用户名、字体大小、是否自动登录

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:@"itcast" forKey:@"username"];
    [defaults setFloat:18.0f forKey:@"text_size"];
    [defaults setBool:YES forKey:@"auto_login"];

    存储文件内容

    NSKeyedArchiver归档(NSCoding)

    • 如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,可以直接用NSKeyedArchiver进行归档和恢复
    • 不是所有的对象都可以直接用这种方法进行归档,只有遵守了NSCoding协议的对象才可以
    • NSCoding协议有2个方法:
      • encodeWithCoder:
        每次归档对象时,都会调用这个方法。一般在这个方法里面指定如何归档对象中的每个实例变量,可以使用encodeObject:forKey:方法归档实例变量
      • initWithCoder:
        每次从文件中恢复(解码)对象时,都会调用这个方法。一般在这个方法里面指定如何解码文件中的数据为对象的实例变量,可以使用decodeObject:forKey方法解码实例变量

    归档一个NSArray对象到Documents/array.archive

    NSArray *array = [NSArray arrayWithObjects:@”a”,@”b”,nil];
    [NSKeyedArchiver archiveRootObject:array toFile:path];
    • 归档成功

      存档文件
    • 恢复(解码)NSArray对象
    NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

    存取过程

    归档Person对象

    • Person.h
    @interface Person : NSObject<NSCoding>
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, assign) int age;
    @property (nonatomic, assign) float height;
    @end
    • Person.m
    @implementation Person
    - (void)encodeWithCoder:(NSCoder *)encoder {
        [encoder encodeObject:self.name forKey:@"name"];
        [encoder encodeInt:self.age forKey:@"age"];
        [encoder encodeFloat:self.height forKey:@"height"];
    }
    - (id)initWithCoder:(NSCoder *)decoder {
        self.name = [decoder decodeObjectForKey:@"name"];
        self.age = [decoder decodeIntForKey:@"age"];
        self.height = [decoder decodeFloatForKey:@"height"];
        return self;
    }
    
    @end
    • 归档(编码)
    Person *person = [[[Person alloc] init] autorelease];
    person.name = @"hosea";
    person.age = 22;
    person.height = 1.83f;
    [NSKeyedArchiver archiveRootObject:person toFile:path];
    • 恢复(解码)
    Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

    NSKeyedArchiver-归档对象的注意

    - 如果父类也遵守了NSCoding协议,请注意:
        - 应该在encodeWithCoder:方法中加上一句
        [super encodeWithCode:encode];
        确保继承的实例变量也能被编码,即也能被归档
        - 应该在initWithCoder:方法中加上一句
        self = [super initWithCoder:decoder];
        确保继承的实例变量也能被解码,即也能被恢复

    归档NSData

    • 使用archiveRootObject:toFile:方法可以将一个对象直接写入到一个文件中,但有时候可能想将多个对象写入到同一个文件中,那么就要使用NSData来进行归档对象

    • NSData可以为一些数据提供临时存储空间,以便随后写入文件,或者存放从磁盘读取的文件内容。可以使用[NSMutableData data]创建可变数据空间


    原理
    • 举个例子:NSData-归档2个Person对象到同一文件中
    • 归档(编码)

      // 新建一块可变数据区
      NSMutableData *data = [NSMutableData data];
      // 将数据区连接到一个NSKeyedArchiver对象
      NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
      // 开始存档对象,存档的数据都会存储到NSMutableData中
      [archiver encodeObject:person1 forKey:@"person1"];
      [archiver encodeObject:person2 forKey:@"person2"];
      // 存档完毕(一定要调用这个方法,调用了这个方法,archiver才会将encode的数据存储到NSMutableData中)
      [archiver finishEncoding];
      // 将存档的数据写入文件
      [data writeToFile:path atomically:YES];
    • 恢复(解码)

      // 从文件中读取数据
      NSData *data = [NSData dataWithContentsOfFile:path];
      // 根据数据,解析成一个NSKeyedUnarchiver对象
      NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
      Person *person1 = [unarchiver decodeObjectForKey:@"person1"];
      Person *person2 = [unarchiver decodeObjectForKey:@"person2"];
      // 恢复完毕(这个方法调用之后,unarchiver不能再decode对象,而且会通知unarchiver的代理调用unarchiverWillFinish:和unarchiverDidFinish:方法)
      [unarchiver finishDecoding];
    • PS:也可将多个对象放入到一个数组中。

      • 将数组进行归档,在数组对象执行archiveRootObject:toFile时,数组中每个对象会自动调用encodeWithCoder:方法进行归档;
      • 相反数组文件进行解档时,在数组对象执行unarchiveObjectWithFile:时,数组中每个对象会自动调用initWithCoder:方法进行解档。

    利用归档实现深复制

    • 比如对一个Person对象进行深复制
    // 临时存储person1的数据
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person1];
    // 解析data,生成一个新的Person对象
    Person *person2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    // 分别打印内存地址
    NSLog(@"person1:0x%x", person1); // person1:0x7177a60
    NSLog(@"person2:0x%x", person2); // person2:0x7177cf0

    深复制原理

    SQLite3

    SQLite3简介

    • SQLite3是一款开源的嵌入式关系型数据库,可移植性好、易使用、内存开销小
    • SQLite3是无类型的,意味着你可以保存任何类型的数据到任意表的任意字段中。比如下列的创表语句是合法的:
       create table t_person(name, age);
      为了保证可读性,建议还是把字段类型加上:
       create table t_person(name text, age integer);
    • SQLite3常用的5种数据类型:text、integer、float、boolean、blob
    • 在iOS中使用SQLite3,首先要添加库文件libsqlite3.dylib和导入主头文件

      导入库
    • 创建或打开数据库

      // path为:~/Documents/person.db
      sqlite3 *db;
      int result = sqlite3_open([path UTF8String], &db);
    • 代码解析:

      • sqlite3_open()将根据文件路径打开数据库,如果不存在,则会创建一个新的数据库。如果result等于常量SQLITE_OK,则表示成功打开数据库
      • sqlite3 *db:一个打开的数据库实例
      • 数据库文件的路径必须以C字符串(而非NSString)传入
    • 关闭数据库:sqlite3_close(db);

    • 执行创表语句

      char *errorMsg;  // 用来存储错误信息
      char *sql = "create table if not exists t_person(id integer primary key autoincrement, name text, age integer);";
      int result = sqlite3_exec(db, sql, NULL, NULL, &errorMsg);
    • 代码解析:

      • sqlite3_exec()可以执行任何SQL语句,比如创表、更新、插入和删除操作。但是一般不用它执行查询语句,因为它不会返回查询到的数据
      • sqlite3_exec()还可以执行的语句:
        1. 开启事务:begin transaction;
        2. 回滚事务:rollback;
        3. 提交事务:commit;

    带占位符插入数据

    char *sql = "insert into t_person(name, age) values(?, ?);";
    sqlite3_stmt *stmt;
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
        sqlite3_bind_text(stmt, 1, "母鸡", -1, NULL);
        sqlite3_bind_int(stmt, 2, 27);
    }
    if (sqlite3_step(stmt) != SQLITE_DONE) {
        NSLog(@"插入数据错误");
    }
    sqlite3_finalize(stmt);
    • 代码解析:
      • sqlite3_prepare_v2()返回值等于SQLITE_OK,说明SQL语句已经准备成功,没有语法问题
      • sqlite3_bind_text():大部分绑定函数都只有3个参数
        1. 第1个参数是sqlite3_stmt *类型
        2. 第2个参数指占位符的位置,第一个占位符的位置是1,不是0
        3. 第3个参数指占位符要绑定的值
        4. 第4个参数指在第3个参数中所传递数据的长度,对于C字符串,可以传递-1代替字符串的长度
        5. 第5个参数是一个可选的函数回调,一般用于在语句执行后完成内存清理工作
      • sqlite_step():执行SQL语句,返回SQLITE_DONE代表成功执行完毕
      • sqlite_finalize():销毁sqlite3_stmt *对象

    查询数据

    char *sql = "select id,name,age from t_person;";
    sqlite3_stmt *stmt;
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            int _id = sqlite3_column_int(stmt, 0);
            char *_name = (char *)sqlite3_column_text(stmt, 1);
            NSString *name = [NSString stringWithUTF8String:_name];
            int _age = sqlite3_column_int(stmt, 2);
            NSLog(@"id=%i, name=%@, age=%i", _id, name, _age);
        }
    }
    sqlite3_finalize(stmt);
    • 代码解析
      • sqlite3_step()返回SQLITE_ROW代表遍历到一条新记录
      • sqlite3_column_*()用于获取每个字段对应的值,第2个参数是字段的索引,从0开始

    Core Data

    Core Data简单介绍

    • Core Data框架提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite3数据库文件中,也能够将保存在数据库中的数据还原成OC对象。在此数据操作期间,不需要编写任何SQL语句。使用此功能,要添加CoreData.framework和导入主头文件CoreData/CoreData.h

      对象-关系映射
    • 在Core Data,需要进行映射的对象称为实体(entity),而且需要使用Core Data的模型文件来描述应用的所有实体和实体属性
    • 这里以Person和Card(身份证)2个实体为例子,先看看实体属性和之间的关联关系

      实体属性和之间的关联关系
      • Person中有个Card属性,Card中有个Person属性
      • 属于一对一双向关联

    模型文件

    • 创建文件

       
    • 添加实体

      这里写图片描述
    • 添加Person实体的基本属性

       
    • 添加Card实体的基本属性

       
    • 在Person中添加card属性

       
    • 在Card中添加person属性

       

      NSManagedObject

    • 通过Core Data从数据库取出的对象,默认情况下都是NSManagedObject对象
    • NSManagedObject的工作模式有点类似于NSDictionary对象,通过键-值对来存取所有的实体属性
      • setValue:forKey: 存储属性值(属性名为key)
      • valueForKey: 获取属性值(属性名为key)

         

        Core Data主要对象


         

        搭建Core Data上下文环境

    • 从应用程序包中加载模型文件
    NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
    • 传入模型,初始化NSPersistentStoreCoordinator
    NSPersistentStoreCoordinator *psc = [[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model] autorelease];
    • 构建SQLite文件路径
    NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSURL *url = [NSURL fileURLWithPath:[docs stringByAppendingPathComponent:@"person.data"]];
    • 添加持久化存储库,这里使用SQLite作为存储库
      NSError *error = nil;
      NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
      if (store == nil) { // 直接抛异常
        [NSException raise:@"添加数据库错误" format:@"%@", [error localizedDescription]];
      }
    • 初始化上下文,设置persistentStoreCoordinator属性
      NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
      context.persistentStoreCoordinator = psc;
      // 用完之后,还是要[context release];

    添加数据

    • 传入上下文,创建一个Person实体对象

      NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
    • 设置简单属性

      [person setValue:@"hosea" forKey:@"name"];
      [person setValue:[NSNumber numberWithInt:22] forKey:@"age"];
    • 传入上下文,创建一个Card实体对象

      NSManagedObject *card = [NSEntityDescription insertNewObjectForEntityForName:@"Card" inManagedObjectContext:context];
      [card setValue:@"4414241933432" forKey:@"no"];
    • 设置Person和Card之间的关联关系

      [person setValue:card forKey:@"card"];
    • 利用上下文对象,将数据同步到持久化存储库

      NSError *error = nil;
      BOOL success = [context save:&error];
      if (!success) {
        [NSException raise:@"访问数据库错误" format:@"%@", [error localizedDescription]];
      }
      // 如果是想做更新操作:只要在更改了实体对象的属性后调用[context save:&error],就能将更改的数据同步到数据库

    查询数据

    • 初始化一个查询请求
      NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
    • 设置要查询的实体
      NSEntityDescription *desc = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
    • 设置排序(按照age降序)
      NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];
      request.sortDescriptors = [NSArray arrayWithObject:sort];
    • 设置条件过滤(name like '%hosea-1%')
      NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@", @"*hosea-1*"];
      request.predicate = predicate;
    • 执行请求
      NSError *error = nil;
      NSArray *objs = [context executeFetchRequest:request error:&error];
      if (error) {
        [NSException raise:@"查询错误" format:@"%@", [error localizedDescription]];
      }
    • 遍历数据
      for (NSManagedObject *obj in objs) {
        NSLog(@"name=%@", [obj valueForKey:@"name"]);
      }

      删除数据

      传入需要删除的实体对象
      [context deleteObject:managedObject];
      将结果同步到数据库
      NSError *error = nil;
      [context save:&error];
      if (error) {
        [NSException raise:@"删除错误" format:@"%@", [error localizedDescription]];
      }

      打开Core Data的SQL日志输出开关


       

      Core Data的延迟加载

    • Core Data不会根据实体中的关联关系立即获取相应的关联对象
    • 比如通过Core Data取出Person实体时,并不会立即查询相关联的Card实体;当应用真的需要使用Card时,才会查询数据库,加载Card实体的信息

    创建NSManagedObject的子类

    • 默认情况下,利用Core Data取出的实体都是NSManagedObject类型的,能够利用键-值对来存取数据
    • 但是一般情况下,实体在存取数据的基础上,有时还需要添加一些业务方法来完成一些其他任务,那么就必须创建NSManagedObject的子类

       
    • 选择模型文件

       
    • 选择需要创建子类的实体

       


       
    • 那么生成一个Person实体对象就应该这样写
    Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
    person.name = @"hosea";
    person.age = [NSNumber numberWithInt:22];
    
    Card *card = [NSEntityDescription insertNewObjectForEntityForName:@”Card" inManagedObjectContext:context];
    card.no = @”4414245465656";
    person.card = card;
     



    文/hosea_zhou(简书作者)
    原文链接:http://www.jianshu.com/p/a3eeae99e902#
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
  • 相关阅读:
    POJ1239
    HDU 2829 四边形不等式优化
    返回数字二进制的最高位位数o(n)
    矩阵快速幂 模板
    HDU4718 The LCIS on the Tree(LCT)
    HDU4010 Query on The Trees(LCT)
    HDU3487 Play With Chains(Splay)
    CF444C DZY Loves Colors
    HDU4836 The Query on the Tree(树状数组&&LCA)
    HDU4831&&4832&&4834
  • 原文地址:https://www.cnblogs.com/yeng/p/5508977.html
Copyright © 2011-2022 走看看