文档章节

iOS开发23:通过归档永久存储数据

双子座
 双子座
发布于 2012/05/12 17:58
字数 1338
阅读 10175
收藏 31

之前一篇文章《iOS开发20:使用Settings Bundle为程序添加设置项》中简单介绍了怎样在Settings程序中设置自己的程序,并实现保存,使得下次运行自己的程序时显示的还是上次的设置项。而上一篇文章介绍SandBox时,我们看到其实使用Settings程序设置后,数据是保存在一个plist文件的。

想要永久保存数据,我们当然可以使用plist文件,当退出程序时,我们执行将数据写入plist文件的操作,使用writeToFile:atomically:方法。

具有这个方法的类有:

NSArray
NSMutableArray
NSDictionary
NSMutableDictionary
NSData
NSMutableData
NSString
NSMutableString
NSNumber
NSDate

例如,我们的数据存储在NSArray的一个对象array中,保存数据时执行:

[array writeToFile:filePath atomically:YES];

其中filePath是放在SandBox中的一个plist文件的完整路径。

不过,使用plist文件还是有局限性的,例如,我们不好将一个图片存储在plist中。

这次的小例子中,我们将会通过归档实现数据的保存。当程序运行时,先检查归档文件是否存在,如果存在的话就从归档文件中读取数据显示在界面上;如果归档文件不存在,就使用默认设置。当程序关闭时,会将数据存储在归档文件中,这样下次运行程序时就会显示上次的设置了。

1、运行Xcode 4.3,新建一个Single View Application,名称为:Archiving Test:

然后将准备好的两张图片添加到工程中。

2、先进行界面设计:

单击ViewController.xib,向其中添加控件:

然后向ViewController.h中为控件建立Outlet映射和Action映射,具体是为所有的TextField、ImageView、UISlider控件和UISwitch控件建立Outlet映射,为Button建立Action映射:

3、新建一个类,用于存储我们的数据:

在菜单栏依次选择File — New — File…,在打开的窗口选择Objective-C Class:

单击Next,输入类名:ArchivingData,选择super class为NSObject:

单击Next,选好位置和分组,点击创建,完成类的建立。

4、打开ArchivingData.h,向其中添加属性,以及协议:

#import <Foundation/Foundation.h>

@interface ArchivingData : NSObject <NSCoding, NSCopying>

@property (copy, nonatomic) UIImage *image;
@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *gender;
@property (copy, nonatomic) NSString *vocation;
@property (copy, nonatomic) NSString *page;
@property float theSlider;
@property BOOL isSwitchOn;

@end

5、打开ArchivingData.m,向其中添加代码:

5.1 在@implementation之前添加代码:

#define kImageKey @"ImageKey"
#define kNameKey @"NameKey"
#define kGenderKey @"GenderKey"
#define kVocationKey @"VocationKey"
#define kPageKey @"PageKey"
#define kTheSliderKey @"TheSliderKey"
#define kIsSwitchOn @"IsSwitchOnKey"

5.2 在@implementation之后添加代码:

@synthesize image;
@synthesize name;
@synthesize gender;
@synthesize vocation;
@synthesize page;
@synthesize theSlider;
@synthesize isSwitchOn;

5.3 在@end之前添加代码:

#pragma mark NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:image forKey:kImageKey];
    [aCoder encodeObject:name forKey:kNameKey];
    [aCoder encodeObject:gender forKey:kGenderKey];
    [aCoder encodeObject:vocation forKey:kVocationKey];
    [aCoder encodeObject:page forKey:kPageKey];
    [aCoder encodeFloat:theSlider forKey:kTheSliderKey];
    [aCoder encodeBool:isSwitchOn forKey:kIsSwitchOn];
    
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        image = [aDecoder decodeObjectForKey:kImageKey];
        name = [aDecoder decodeObjectForKey:kNameKey];
        gender = [aDecoder decodeObjectForKey:kGenderKey];
        vocation = [aDecoder decodeObjectForKey:kVocationKey];
        page = [aDecoder decodeObjectForKey:kPageKey];
        theSlider = [aDecoder decodeFloatForKey:kTheSliderKey];
        isSwitchOn = [aDecoder decodeBoolForKey:kIsSwitchOn];
    }
    return self;
}

5.4 在@end之前添加代码:

#pragma mark NSCoping
- (id)copyWithZone:(NSZone *)zone {
    ArchivingData *copy = [[[self class] allocWithZone:zone] init];
    copy.image = self.image;
    copy.name = [self.name copyWithZone:zone];
    copy.gender = [self.gender copyWithZone:zone];
    copy.vocation = [self.vocation copyWithZone:zone];
    copy.page = [self.page copyWithZone:zone];
    copy.theSlider = self.theSlider;
    copy.isSwitchOn = self.isSwitchOn;
    return copy;
}

在ArchivingData类中,我们添加了几个属性,这些属性与上面创建的控件是一一对应的。之后实现了几个协议方法,这些方法分别用于编码、解码和复制。

6、打开ViewController.h,向其中添加属性和方法:

@property (copy, nonatomic) NSString *archivingFilePath;

- (void)applicationWillResignActive:(NSNotification *)notification;

7、打开ViewController.m,添加代码:

7.1 在@implementation之后添加代码:

@synthesize archivingFilePath;

7.2 在#import之后添加代码:

#import "ArchivingData.h"

#define kArchivingFileKey @"archivingFile"
#define kArchivingDataKey @"ArchivingDataKey"

7.3 在viewDidLoad方法中添加代码:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    self.archivingFilePath = [documentsDirectory stringByAppendingPathComponent:kArchivingFileKey];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    if ([fileManager fileExistsAtPath:self.archivingFilePath]) {
        //如果归档文件存在,则读取其中内容,显示在界面上
        NSData *data = [[NSMutableData alloc] initWithContentsOfFile:self.archivingFilePath];
        NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
        ArchivingData *archivingData = [unarchiver decodeObjectForKey:kArchivingDataKey];
        [unarchiver finishDecoding];
        theImageView.image = archivingData.image;
        nameTextField.text = archivingData.name;
        genderTextField.text = archivingData.gender;
        vocationTextField.text = archivingData.vocation;
        pageTextField.text = archivingData.page;
        theSlider.value = archivingData.theSlider;
        theSwitch.on = archivingData.isSwitchOn;
    } else {
        //如果归档文件不存在,则设置imageView为boy.png
        theImageView.image = [UIImage imageNamed:@"boy.png"];
    }
    
    //当程序进入后台时,将当前设置项写入归档文件
    UIApplication *app = [UIApplication sharedApplication];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationWillResignActive:)
                                                 name:UIApplicationWillResignActiveNotification
                                               object:app];
}

7.4 找到switchImage方法,添加代码:

- (IBAction)switchImage:(id)sender {
    UIImage *image1 = [UIImage imageNamed:@"boy.png"];
    UIImage *image2 = theImageView.image;
    if (![image1 isEqual:image2]) {
        theImageView.image = image1;
    } else {
        theImageView.image = [UIImage imageNamed:@"gemini.png"];
    }
}

7.5 在@end之前添加代码:

//程序进入后台时,保存设置
- (void)applicationWillResignActive:(NSNotification *)notification {
    ArchivingData *archivingData = [[ArchivingData alloc] init];
    archivingData.image = self.theImageView.image;
    archivingData.name = self.nameTextField.text;
    archivingData.gender = self.genderTextField.text;
    archivingData.vocation = self.vocationTextField.text;
    archivingData.page = self.pageTextField.text;
    archivingData.theSlider = theSlider.value;
    archivingData.isSwitchOn = theSwitch.on;
    NSMutableData *data = [[NSMutableData alloc] init];
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    [archiver encodeObject:archivingData forKey:kArchivingDataKey];
    [archiver finishEncoding];
    [data writeToFile:self.archivingFilePath atomically:YES];
}

8、最后,为了使得键盘可以关闭,我们还要添加关闭键盘的操作,参考《iOS开发4:关闭键盘》中的第2步。

9、运行程序

刚运行程序如下左图:

  

我们添加一些数据,更换头像,再调整Silder和Switch,如上图右。

之后,按模拟器上的Home建,使得程序在后台运行。

此时,查看程序的SandBox,可以看到程序的Documents目录下出现了文件archivingFile:

之后使用Xcode结束运行,再运行程序。程序第二次运行时,显示如上图左,这说明我们实现了数据的永久存储。

 

© 著作权归作者所有

双子座
粉丝 540
博文 78
码字总数 61009
作品 0
南京
程序员
私信 提问
加载中

评论(11)

唐丽梅
唐丽梅
写的很好。。对我有用处!
wwwang89
wwwang89
大侠,求demo一份,小弟感激不尽,,,wwwang89@163.com,谢谢!!
steven.lee
steven.lee
是我看错了 悲催

有可能是路径不对。要是将NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);写成NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES );就会出现你说的这种情况
steven.lee
steven.lee
大哥 你貌似没有创建存储数据文件啊 在判断是否存在数据文件前面是不是需要加上

if(![fileManager fileExistsAtPath:self.archivingFilePath])
{
[[NSFileManager defaultManager] createFileAtPath:archivingFilePath contents:nil attributes:nil];
}
淡竹丝

引用来自“夏柳柳”的评论

按照步骤做,但是沙河目录里没有存储数据啊??求解啊。。。

有可能是路径不对。要是将NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);写成NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES );就会出现你说的这种情况
双子座
双子座 博主

引用来自“zk0301”的评论

归档文件和第2章的普通的.plist文件有什么不同,我看都有读和写操作呀。一般开发常用哪一个?

看你的数据时什么格式了
z
zk0301
归档文件和第2章的普通的.plist文件有什么不同,我看都有读和写操作呀。一般开发常用哪一个?
夏柳柳
按照步骤做,但是沙河目录里没有存储数据啊??求解啊。。。
terrysunhh
terrysunhh
有个问题请教一下:
ArchivingData中引用类型的属性的都声明了copy属性,在5.4中为什么copy.image = self.image;
而 copy.name = [self.name copyWithZone:zone]; 要用copyWithZone方法?
y
yangdefeng
顶,楼主。真的是受益匪浅
浅谈iOS开发中的对象归档

iOS提供的数据持久化方式有:SQLiteCoreData属性列表、NSUserDefault对象归档。 这里来简单介绍下iOS开发中的对象归档: 对象归档是将对象归档以文件的形式保存到磁盘中(也称为序列化,持久化...

泊菜
2014/11/27
4K
8
iOS应用存储数据的常用的方式之plist和归档

iOS应用创建的数据存储方式 plist文件,是XML属性的列表形式 归档(遵循协议) SQLite(需要自己写sql语句) Core Data 苹果对的一个封装 今天我们先来说说文件,需要知道一个概念 沙盒文件 每一个...

追逐iOS
04/20
0
0
用 Ruby 开发 iOS 应用 —— RubyMotion

RubyMotion是一个商业应用,基于开源的 MacRuby, 让你可以使用 Ruby 语言来开发 iOS 应用程序! RubyMotion 应用的创建和后期维护都是采用终端命令行方式,一个 RubyMotion 项目是基于 Rake...

红薯
2012/05/04
7.2K
16
腾讯开源基于 mmap 的高性能 key-value 组件 MMKV

腾讯微信团队宣布开源 MMKV ,这是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,主打高性能和稳定性。MMKV 从 2015 年中至今,在 iOS 微信上使用已有近 3 ...

王练
2018/09/22
3.7K
10
iOS 8 正式版发布!有史以来极其重大的 iOS 版本

苹果正式发布了 iOS 8 正式版。iPhone 4s 或更新设备,iPad 2 或更新设备以及第五代 iPod touch 或 iPad mini 可以升级至 iOS 8。用户可以通过设置应用中的通用——软件更新完成升级。iOS 8...

oschina
2014/09/18
5.1K
25

没有更多内容

加载失败,请刷新页面

加载更多

springboot2.0 maven打包分离lib,resources

springboot将工程打包成jar包后,会出现获取classpath下的文件出现测试环境正常而生产环境文件找不到的问题,这是因为 1、在调试过程中,文件是真实存在于磁盘的某个目录。此时通过获取文件路...

陈俊凯
今天
5
0
BootStrap

一、BootStrap 简洁、直观、强悍的前端开发框架,让web开发更加迅速、简单 中文镜像网站:http://www.bootcss.com 用于开发响应式布局、移动设备优先的WEB项目 1、使用boot 创建文件夹,在文...

wytao1995
今天
9
0
小知识:讲述Linux命令别名与资源文件的区别

别名 别名是命令的快捷方式。为那些需要经常执行,但需要很长时间输入的长命令创建快捷方式很有用。语法是: alias ppp='ping www.baidu.com' 它们并不总是用来缩短长命令。重要的是,你将它...

老孟的Linux私房菜
今天
8
0
《JAVA核心知识》学习笔记(6. Spring 原理)-5

它是一个全面的、企业应用开发一站式的解决方案,贯穿表现层、业务层、持久层。但是 Spring 仍然可以和其他的框架无缝整合。 6.1.1. Spring 特点 6.1.1.1. 轻量级 6.1.1.2. 控制反转 6.1.1....

Shingfi
今天
7
0
Excel导入数据库数据+Excel导入网页数据【实时追踪】

1.Excel导入数据库数据:数据选项卡------>导入数据 2.Excel导入网页数据【实时追踪】:

东方墨天
今天
10
1

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部