zoukankan      html  css  js  c++  java
  • IOS笔记048-数据存储

     

    IOS数据存储的几种方式

            XML属性列表(plist)

            归档 Preference(偏好设置) 

            NSKeyedArchiver归档(NSCoding)

            SQLite3

            Core Data

    先来研究一下前三种。

            每个iOS应用都有自己的应用沙盒(应用沙盒就是文件系统目录),与其他文件系统隔离。应用必须待在自己的沙盒里,其他应用不能访问该沙盒。沙盒的结构如下所示:

    屏幕快照 2015 06 13 19 05 06屏幕快照 2015 06 13 19 08 29

           应用程序包:(上图中的QQ.app)包含了所有的资源文件和可执行文件。
           Documents:保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录。
           tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录。
           Library/Caches:保存应用运行时生成的需要持久化的数据,iTunes同步设备时不会备份该目录。一般存储体积大、不需要备份的非重要数据。
           Library/Preference:保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录。

    每个目录的获取方法

               // 临时目录

                NSString *path = NSTemporaryDirectory();

                // /Users/song/Library/Developer/CoreSimulator/Devices/B040D34E-CF0E-41C2-9BD0-C309566CC349/data/Containers/Data/Application/A8519BA5-FB34-4B31-BB72-3948264AFB37/tmp/

                // 主目录

                NSString *path = NSHomeDirectory();

                //  /Users/song/Library/Developer/CoreSimulator/Devices/B040D34E-CF0E-41C2-9BD0-C309566CC349/data/Containers/Data/Application/E6A8A443-55CA-463B-886C-DAAF69E8F3BC

                

                // Documents 目录,第三个参数为yes显示全路径,为NO时显示位波浪线 “~”

                NSString *path1 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectoryNSUserDomainMaskYES)[0];

                // /Users/song/Library/Developer/CoreSimulator/Devices/B040D34E-CF0E-41C2-9BD0-C309566CC349/data/Containers/Data/Application/61B033AA-BBFB-4A3A-AD6E-2E332B78C2ED/Documents

                NSString *path1 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO)[0];

                //  ~/Documents

                // cache 目录

                NSString *path2 =NSSearchPathForDirectoriesInDomains(NSCachesDirectoryNSUserDomainMaskYES)[0];

                // /Users/song/Library/Developer/CoreSimulator/Devices/B040D34E-CF0E-41C2-9BD0-C309566CC349/data/Containers/Data/Application/1B4923AF-4B1F-4C7A-941E-D3621D7945C3/Library/Caches

                // PreferencePanes 目录

                NSString *path2 =NSSearchPathForDirectoriesInDomains(NSPreferencePanesDirectory, NSUserDomainMask, YES)[0];

                // /Users/song/Library/Developer/CoreSimulator/Devices/B040D34E-CF0E-41C2-9BD0-C309566CC349/data/Containers/Data/Application/D654173E-DC23-4951-8C28-016878B5246B/Library/PreferencePanes

    还有一个特殊的单例对象用来保存用户设置

                // 默认保存到PreferencePanes目录

                [[NSUserDefaults standardUserDefaultssetObject:@“hah” forKey:@"1"];

                [[NSUserDefaults standardUserDefaultssetBool:YES forKey:@“2”];

    1、plist文件

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

            如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,就可以使用writeToFile:atomically:方法直接将对象写到属性列表文件中.

            保存数据

                // plist保存数据

                // 保存用户名、密码、记住密码、自动登录

                NSMutableDictionary *dict = [NSMutableDictionarydictionary];

                [dict setObject:_userField.text forKey:@"user"];

                [dict setObject:_pwdField.text forKey:@"pwd"];

                [dict setObject:@(_rmbPwdSwitch.on) forKey:@"rmb"];

                [dict setObject:@(_autoLoginSwitch.on) forKey:@"auto"];

                // 获取cache路径

                NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectoryNSUserDomainMaskYES)[0];

                // 取得文件的全路径

                NSString *filePath = [cachePath stringByAppendingPathComponent:@"userInfo.plist"];

                // 写入到文件

                [dict writeToFile:filePath atomically:YES];

     屏幕快照 2015 06 13 19 49 39

    屏幕快照 2015 06 13 19 49 57

           读取数据

        // 读取plist中数据,显示在textField

        // 获取cache路径

        NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectoryNSUserDomainMaskYES)[0];

        // 取得文件的全路径

        NSString *filePath = [cachePath stringByAppendingPathComponent:@"userInfo.plist"];

        // 读取字典数据

        NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];

        // 给控件赋值

        NSString *userName = dict[@"user"];

        NSString *pwd  = dict[@"pwd"];

        BOOL rmb =  dict[@"rmb"];

        BOOL autoLogin = dict[@"autoLogin"];

        

        _userField.text = userName;

        

        if (rmb == YES)

        {

            _pwdField.text = pwd;

        }

     

        _rmbPwdSwitch.on = rmb;

        _autoLoginSwitch.on = autoLogin;

    这里目前还有问题,那就是读取出来的bool变量全是YES。等明天研究一下再。

     问题出来了,读取字典中得bool变量时要转换一下

        // 给控件赋值

        NSString *userName = dict[@"user"];

        NSString *pwd  = dict[@"pwd"];

        BOOL rmbPwd =  [dict[@"rmb"boolValue]; // 要转换一下,不然读取结果不准确

        BOOL login = [dict[@"autoLogin"boolValue];

    2、Preference 偏好设置

    内部实现也是一个plist文件。不过是否系统控制路径和名称。只需写入和读取就行,不用关系细节。

    写入数据

                // preference 偏好设置

                 NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults]; // 单例对象

                [userDefault setObject:_userField.text forKey:@"user"];

                [userDefault setObject:_pwdField.text forKey:@"pwd"];

                [userDefault setBool:_rmbPwdSwitch.on forKey:@"rmb"];

                [userDefault setBool:_autoLoginSwitch.on forKey:@"autoLogin"];

     读取数据

        // 偏好设置,读取数据,

        NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];

        NSString *name = [userDefault stringForKey:@"user"];

        NSString *pwd = [userDefault stringForKey:@"pwd"];

        BOOL rmbPwd = [[userDefault boolForKey:@“rmb”] boolValue];

        BOOL login = [[userDefault boolForKey:@“autoLogin”] boolValue];

        // 给控件赋值

        _userField.text = name;

        if (rmbPwd == YES)

        {

            _pwdField.text = pwd; // 记住密码才赋值

        }

        _rmbPwdSwitch.on = rmbPwd;

        _autoLoginSwitch.on = login;

        if (login) { // 自动登录

            

            [self login:nil];

        }

    注意:UserDefaults设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。所以调用了set方法之后数据有可能还没有写入磁盘应用程序就终止了。出现以上问题,可以通过调用synchornize方法强制写入
    [defaults synchornize];

     3、NSKeyedArchiver 归档

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

        1、常见类型

    写入常用类型

                // 归档对象NSKeyedArchiver

                // 获取路径

                NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectoryNSUserDomainMaskYES)[0];

                // 取得文件的全路径

                NSString *filePath = [cachePath stringByAppendingPathComponent:@"info.data"];

                

    //            [NSKeyedArchiver archiveRootObject:_userField.text toFile:filePath];

                NSArray *all = @[_userField.text,_pwdField.text,@(_rmbPwdSwitch.on),@(_autoLoginSwitch.on)];

                [NSKeyedArchiver archiveRootObject:all toFile:filePath];

    读取

        // 获取路径

        NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectoryNSUserDomainMaskYES)[0];

        // 取得文件的全路径

        NSString *filePath = [cachePath stringByAppendingPathComponent:@"info.data"];

        // 归档对象NSKeyedArchiver

    //    NSString *name =  [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];

        NSArray *all = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];

        NSLog(@"%@",all);

        2、自定义对象

     要读写自定义对象,自定义对象要遵守NSCoding协议,并实现两个方法,一个打包initWithCoder:,一个解包encodeWithCoder:

     以联系人为例

        #import <Foundation/Foundation.h>

     

        @interface SLQContact : NSObject <NSCoding>

     

        /*姓名*/

        @property (strongnonatomicNSString *name;

        // 手机号

        @property (strongnonatomicNSString *phone;

     

        + (instancetype)contactWithName:(NSString *)name andPhone:(NSString *)phone;

        @end

    实现协议方法 

        #import "SLQContact.h"

     

        @implementation SLQContact

     

        + (instancetype)contactWithName:(NSString *)name andPhone:(NSString *)phone

        {

            SLQContact *contact = [[self allocinit];

            contact.name = name;

            contact.phone = phone;

            return  contact;

        }

        // 解包,按照key找属性

        - (void)encodeWithCoder:(NSCoder *)aCoder

        {

            [aCoder encodeObject:self.name forKey:@"name"];

            [aCoder encodeObject:self.phone forKey:@"phone"];

        }

        // 打包,就是给属性指定一个key

        - (id)initWithCoder:(NSCoder *)aDecoder

        {

            if (self = [super init])

            {

                self.name = [aDecoder decodeObjectForKey:@"name"];

                self.phone = [aDecoder decodeObjectForKey:@"phone"];

            }

            return  self;

        }

        @end 

    然后在联系人控制器中读写归档文件

    定义一个宏用来获取文件路径

    // 获得归档文件的路径

    #define SLQFilePath [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"info.data"]

    写如归档文件

        // 添加按钮,按下后进入添加联系人界面

        - (IBAction)addBtn:(id)sender {

            //

        //    [self performSegueWithIdentifier:@"contactToAdd" sender:nil];

            // 通过代码获取storyboard中得控制器

            UIStoryboard *story = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

            // 获取mainstoryboard中得控制器,以标识符区分

            SLQAddViewController* addVC = [story instantiateViewControllerWithIdentifier:@"add"];

            // block封装待执行的代码

            addVC.block= ^(SLQContact *contact){

                // 更新模型

                [self.contacts addObject:contact];

                // 刷新表格

                [self.tableView reloadData];

                // 3.保存联系人,注意:如果归档数组,底层会遍历数组元素一个一个归档

                [NSKeyedArchiverarchiveRootObject:self.contactstoFile:SLQFilePath];

            };

            // 跳转到添加联系人界面

            [self.navigationController pushViewController:addVC animated:YES];

        } 

     读取归档文件,在属性contacts的setter方法中进行读取。

        // 重写set方法

        - (NSMutableArray *)contacts

        {

            if (_contacts == nil)

            {

                // 读取归档文件

                _contacts = [NSKeyedUnarchiver unarchiveObjectWithFile:SLQFilePath];

                if (_contacts == nil) { // 如果读取的文件为空,初始化数组

                      _contacts = [NSMutableArray array];

                }

            }

            return _contacts;

        }

     

    还有两种存储方式,属于数据库范畴,继续学习。

     

     

  • 相关阅读:
    Linux驱动下的platform总线架构(转)
    一生中很值得看的30个故事之一断箭
    学习嵌入式LINUX系统的笔记和体会
    DIY:自己动手做一个迷你 Linux 系统
    linux里面i386 i686 i486 i586代表什么?是什么意思
    菜鸟编译Linux内核
    LINUX核心重编与升级
    ARM 内核移植中常见的错误
    Linux 2.6.19.x 内核编译配置选项简介
    基于S3C2410平台移植Linux 2.6内核指南
  • 原文地址:https://www.cnblogs.com/songliquan/p/4574913.html
Copyright © 2011-2022 走看看