保存,加载,及应⽤用程序状态范圣刚,princetoad@gmail.com www.tfan.org
• 在⼀一个 iOS application 中保存和加载数据有很多 ⽅方法。• 这⼀一个主题我们来看⼀一下⼀一些最常⻅见的机制,以 及在 iOS 上对⽂文件系统进⾏行读写需要理解的概念
Archiving
• 任何 iOS 应⽤用实际都在做⼀一件事情:提供给⽤用户⼀一个界⾯面让他们来操作数据。• 在应⽤用中的每⼀一个对象在这个过程中都扮演下⾯面的⼀一个⾓角⾊色。• Model 对象,负责持有⽤用户操作的数据• View 对象,只是体现这些数据• ...
• 在 Homepwner 中,⽤用户操作的模型对象是BNRItem 的实例。• 如果在应⽤用退出后再启动时 BNRItem 的实例能够持久存在,Homepwner 将真正成为⼀一个有⽤用的应⽤用• 在这⼀一章,我们将使⽤用 archiving...
• Archiving 是在 iOS 上持久化模型数据的⼀一种最常 ⻅见的⽅方法。• Archiving ⼀一个对象会记录它所有的实例变量并且 把他们保存到⽂文件系统。• Unarchiving ⼀一个对象就是从⽂文件系统加载数据, 并且再从这...
NSCoding • 需要把它的实例进⾏行 archive 和 unarchive 的类必    须符合 NSCoding protocol。 • 并且实现它的两个必须的⽅方法,    encodeWithCoder: 和 initWithCo...
encode • 在 BNRItem.h 中增加 NSCoding protocol 的声明@interface BNRItem : NSObject <NSCoding>• 然后是实现两个必须的⽅方法,⾸首先是  encodeWithCoder:
• 当 BNRItem 被发送 encodeWithCoder: 消息时,它 将把它的所有的实例变量编码到作为⼀一个参数传 ⼊入的 NSCoder 对象中• 我们可以把 NSCoder 对象想成⼀一个数据容器,负 责组织这些数据• NSCode...
encodeWithCoder:    • 在 BNRItem.m 中实现 encodeWithCoder: 以添加实     例变量到这个容器- (void)encodeWithCoder:(NSCoder *)aCoder{    [aCo...
encodeXXX:forKey• 指向对象的指针使⽤用 encodeObject:forKey: 编码• valueInDollars 使⽤用 encodeInt:forKey: 编码,还有 很可以 encode 的类型
• 不管被编码的值是什么类型,总有⼀一个 key 存在• key 是⼀一个⽤用来标识哪个实例变量在被编码的字符串。按照惯例,这个 key 就是被编码的实例变量的名字
编码对象的递归过程• ⼀一个对象被编码时,被发送 encodeWithCoder: 。 当⼀一个对象被发送 encodeWithCoder:,它⽤用同样 的⽅方式编码它的实例变量 - 也是给它们发送 encodeWithCoder:• 这样编码...
编码⼀一个对象• 想要被编码,这些对象必须也符合 NSCoding• NSDate, NSString 都是 NSCoding 兼容的
initWithCoder:• encoding 时使⽤用 key 的⺫⽬目的是为了之后在 BNRItem 被从⽂文件系统加载时⽤用来获取已编码的 值• 被从⼀一个 archive 中加载的对象被发送的是 initWithCoder: 消息。•...
decodeXXXForKey:- (id)initWithCoder:(NSCoder *)aDecoder{    self = [super init];    if (self) {        [self setItemName:[...
• 经过前⾯面的改造之后,BNRItem 现在是 NSCoding 兼容的了,⽽而且也可以使⽤用 archiving 从⽂文件系统 进⾏行保存和加载。• 现在还有两个问题要考虑: • 我们需要⼀一种⽅方法来开启保存和加载操作?(现在只  是可以...
Application Sandbox
• 每⼀一个 iOS 应⽤用都有它⾃自⼰己的 applicaton sandbox(应⽤用沙箱)。• ⼀一个 application sandbox 是在⽂文件系统上的⼀一个 和⽂文件系统其余部分隔离的⺫⽬目录。• 应⽤用必须位于这个 sand...
Application Sandbox
application sandbox• Library/Preferences/• tmp/• Documents/• Library/Caches
Library/Preferences/• 所有的⾸首选项都存在这⾥里;“Settings”应⽤用也在这 ⾥里查找应⽤用程序⾸首选项• Library/Preferences 被 NSUserDefaults 类⾃自动化的 进⾏行处理• 当设备...
tmp/• ⽤用于写⼊入应⽤用运⾏行时临时⽤用到的数据,⽤用完以后 应该从这个⺫⽬目录删除• 不备份• 使⽤用函数 NSTemporaryDirectory 拿到 application sandbox 中 tmp ⺫⽬目录的路径
Documents• 需要持久化存储的数据可以写到这⾥里• 备份
Library/Caches• 应⽤用程序⽣生成的,还希望能够持久存在• 不会被备份• 不被备份的⼀一个主要的原因就是这些⽂文件可能会 很⼤大,会延⻓长同步设备的时间
构造⼀一个⽂文件路径• 来⾃自 Homepwner 的 BNRItem 将被保存到 Documents ⺫⽬目录中⼀一个单独的⽂文件中• BNRItemStore 将处理⽂文件写⼊入和读取的⼯工作• ⾸首先,我们需要来构建这个⽂文件的路径• 在...
- (NSString *)itemArchivePath{    NSArray *documentDirectories =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, N...
NSKeyedArchiver 和NSKeyedUnarchiver
• 现在我们已经有了⼀一个⽂文件系统的路径,同时模    型对象(BNRItem)也可以被保存到⽂文件系统了 • 我们可以使⽤用 NSKeyedArchiver 在应⽤用 “退出” 时    保存 BNRItems • 在 BNRItemSto...
archiveRootObject:- (BOOL)saveChanges{    // 返回成功或失败    NSString *path = [self itemArchivePath];    return [NSKeyedArchive...
• 这个⽅方法会⾸首先创建⼀一个 NSKeyedArchiver 的实 例。• 然后发送 encodeWithCoders: 消息到 root object(这⾥里是 allItems )。• NSKeyedArchiver 是 NSCoder...
• allItems 收到 encodeWithCoder: 消息后,会再发 给它包含的所有的对象,传递的同样是 NSKeyedArchiver。• 这个数组的内容,也就是⼀一堆 BNRItems,再 encode 它们的实例变量到同⼀一个 N...
Archiving
applicationDidEnterBackGround: • 当⽤用户点击 “Home” 时,   applicationDidEnterBackground: 消息被发给   HomepwnerAppDelegate,我们希望在这时候给 ...
⽂文件写⼊入确认• 构建并在模拟器中运⾏行,创建⼀一个新的 BNRItems。然后点击 home 键离开应⽤用,检查控 制台,我们应该看到前⾯面代码中记录的⽇日志。• 我们也可以在电脑的⺫⽬目录中确认⼀一下⽂文件是否写 ⼊入成功。在 Finde...
• 下⼀一步编写加载⽂文件的代码,我们可以在BNRItemStore 被创建时使⽤用 NSKeyedUnarchiver
unarchiveObjectWithFile:- (id)init{    self = [super init];    if (self) {//         allItems = [[NSMutableArray alloc] in...
• 下⾯面我们来看⼀一下加载的过程。• 检测 archive 中 root 对象的类型并且⽣生成这个类型 的实例,在这⾥里,这个类型将是⼀一个 NSMutableArray,因为我们就是使⽤用这个类型的 root object 来⽣生成的这个 ...
• 然后新分配的 NSMutableArray 会被发送 initWithCoder: , NSKeyedUnarchiver 被作为参数传 ⼊入;• 然后就是数组开始从 NSKeyedUnarchiver 解码它 的内容( BNRItem 的...
不⽣生成随机数据了    • 因为我们现在已经可以保存和加载 BNRItem 了,     所以我们可以把⽣生成随机数据的功能删除掉。    • 在 BNRItemStore.m 中修改 createItem 的实现让它     ⽣生成⼀一个不...
应⽤用程序的 States 和 Transitions
• 在 Hompwner 中,我们是在应⽤用程序进⼊入 backgroud state 时 archive BNRItem 的。• 下⾯面我们来看⼀一下应⽤用程序状态有关的内容,包括这 些状态是怎么被触发的,以及我们怎么获得通知• 后⾯面是⼀一...
应⽤用状态
Not running• 不执⾏行任何代码也不占⽤用 RAM 内存
active state• ⽤用户启动⼀一个应⽤用之后,就进⼊入了 active state 。• 应⽤用界⾯面在屏幕上显⽰示,正在接收事件,并且它 的代码在处理这些事件
inactive state• 在 active state 下,应⽤用可以被系统事件临时性的 打断,例如 SMS 消息,推送通知,来电,或者 alarm。应⽤用上⾯面会被叠加⼀一个界⾯面来处理这个事 件。这种状态被称为 inactive st...
• 在 inactive state 下,应⽤用⼤大部分是可⻅见的,并且 在执⾏行代码,但是没有在接收事件。应⽤用⼀一般在 inactive state 下会停留很短的⼀一个时间。• 可以通过按下设备顶部的锁定按钮(lock)让活 动的应⽤用程...
background state• 当⽤用户按下 home 键或是某种其他⽅方式切换到其 它应⽤用时,应⽤用进⼊入 background state。• 在 background state 下,界⾯面不可⻅见,也不能接 收消息,但是仍然可以执...
suspended state• 在 suspended state 下的应⽤用不能执⾏行代码,⽆无法 看到它的界⾯面,并且 suspended 时不需要的资源 都会被销毁。
• suspended 的应⽤用处于冻结状态,当⽤用户重新启动它的时候,可以被快速的解冻。• 被销毁的资源都是可以被重新加载的,像缓存的图⽚片,系统管理的缓存,以及其他图⽚片数据。• 我们不需要考虑这些资源的销毁和重新记载,应⽤用程序会⾃自动处理。
应⽤用状态表                         Receives   Executes   State       Visible                          Events      Code Not Run...
background 时保存数据• 应⽤用变更状态时,应⽤用 delegate 会被发送⼀一个消 息。像 application:didFinishLaunchingWithOptions: 等。• 我们可以在这些⽅方法中实现代码采取⼀一些适当...
• 我们应该在往 background state 转换时保存关键 的修改和应⽤用程序的状态。• 因为这是应⽤用程序在进⼊入 suspended state 前还可 以执⾏行代码的最后时刻。• ⼀一旦进⼊入了 suspended state,应...
使⽤用 NSData 写⼊入⽂文件系统
• 对 Homepwner 进⾏行 archiving 时我们保存和加载 了针对每个 BNRItem 的 imageKey。• 我们可以扩展 image store,在它们被添加时保存 图⽚片,然后在需要的时候可以提取出来。
• BNRItem 实例的 image 是通过⽤用户交互⽣生成的, 并且只存储在应⽤用程序内部。• 这样 Documents ⺫⽬目录就是保存它们的绝好位置。• ⽤用户采集图⽚片时我们⽣生成了⼀一个唯⼀一的 image key,我们可以使⽤用这...
图⽚片路径:imagePathForKey:     • 在 BNRImageStore.h 中,添加⼀一个新的⽅方法声明     • 并在 BNRImageStore.m 中实现,可以使⽤用给定的        key 在 documents...
NSData• 为了保存和加载,我们需要把图⽚片的 JPEG 形式拷 ⻉贝到内存中的 buffer 。• NSData 就可以⽅方便的⽣生成,维护和销毁这些类型 的 buffer。• ⼀一个 NSData 实例持有⼀一定数量字节数的⼆二进制数 据。...
UIImageJPEGPresentation • 在 BNRImageStore.m 中修改 setImage:forKey: ⽅方法    来获得⼀一个路径,并且保存图⽚片- (void)setImage:(UIImage *)i forK...
writeToFile:automatically:     NSData *d = UIImageJPEGRepresentation(i, 0.5);     [d writeToFile:imagePath atomically:YES]...
• automatically 是⼀一个 Boolean 值。• 如果是 YES, ⽂文件会先被写到⽂文件系统的⼀一个临时 位置,⼀一些写⼊入操作完成,这个⽂文件再被使⽤用它 的 path 重命名,已经存在的⽂文件会被替掉。• 设成 autom...
同步删除    • 增加完⽂文件以后,再看⼀一下⽂文件的删除。    • 在 BNRImageStore.m 中,我们要确保把⽂文件从     store 中删除的同时,也把它从⽂文件系统中删除。- (void)deleteImageForke...
imageWithContentsOfFile:• 现在⽂文件已经被存储在⽂文件系统了, BNRImageStore 在被请求的时候需要加载这些⽂文 件。• UIImage 的类⽅方法 imageWithContentsOfFile: 可以 从...
imageForKey:// 如果还没有⽂文件的话,从⽂文件系统加载- (UIImage *)imageForKey:(NSString *)s{//    return [dictionary objectForKey:s];    // 如...
• 前⾯面对 image 的增加,删除和记载都完成了,还     有⼀一个问题是 BNRItem 被从 store 中删除时,⽂文件     也应该被从⽂文件系统删除。    • 在 BNRItemStore.m 中,导⼊入 BNRImageS...
Upcoming SlideShare
Loading in …5
×

14 Saving Loading and Application States

648 views

Published on

保存,加载和程序状态

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
648
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

14 Saving Loading and Application States

  1. 1. 保存,加载,及应⽤用程序状态范圣刚,princetoad@gmail.com www.tfan.org
  2. 2. • 在⼀一个 iOS application 中保存和加载数据有很多 ⽅方法。• 这⼀一个主题我们来看⼀一下⼀一些最常⻅见的机制,以 及在 iOS 上对⽂文件系统进⾏行读写需要理解的概念
  3. 3. Archiving
  4. 4. • 任何 iOS 应⽤用实际都在做⼀一件事情:提供给⽤用户⼀一个界⾯面让他们来操作数据。• 在应⽤用中的每⼀一个对象在这个过程中都扮演下⾯面的⼀一个⾓角⾊色。• Model 对象,负责持有⽤用户操作的数据• View 对象,只是体现这些数据• Controllers,负责应⽤用运⾏行到底是怎么回事• 因此当我们讨论保存和加载数据时,我们⼏几乎总是在谈论保存和加载 Model 对象
  5. 5. • 在 Homepwner 中,⽤用户操作的模型对象是BNRItem 的实例。• 如果在应⽤用退出后再启动时 BNRItem 的实例能够持久存在,Homepwner 将真正成为⼀一个有⽤用的应⽤用• 在这⼀一章,我们将使⽤用 archiving 来保存和加载BNRItem
  6. 6. • Archiving 是在 iOS 上持久化模型数据的⼀一种最常 ⻅见的⽅方法。• Archiving ⼀一个对象会记录它所有的实例变量并且 把他们保存到⽂文件系统。• Unarchiving ⼀一个对象就是从⽂文件系统加载数据, 并且再从这个记录⽣生成对象。
  7. 7. NSCoding • 需要把它的实例进⾏行 archive 和 unarchive 的类必 须符合 NSCoding protocol。 • 并且实现它的两个必须的⽅方法, encodeWithCoder: 和 initWithCoder:@protocol NSCoding- (void)encodeWithCoder:(NSCoder *)aCoder;- (id)initWithCoder:(NSCoder *)aDecoder;
  8. 8. encode • 在 BNRItem.h 中增加 NSCoding protocol 的声明@interface BNRItem : NSObject <NSCoding>• 然后是实现两个必须的⽅方法,⾸首先是 encodeWithCoder:
  9. 9. • 当 BNRItem 被发送 encodeWithCoder: 消息时,它 将把它的所有的实例变量编码到作为⼀一个参数传 ⼊入的 NSCoder 对象中• 我们可以把 NSCoder 对象想成⼀一个数据容器,负 责组织这些数据• NSCoder 以key-value pair 的⽅方式组织数据
  10. 10. encodeWithCoder: • 在 BNRItem.m 中实现 encodeWithCoder: 以添加实 例变量到这个容器- (void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:itemName forKey:@"itemName"]; [aCoder encodeObject:serialNumber forKey:@"serialNumber"]; [aCoder encodeObject:dateCreated forKey:@"dateCreated"]; [aCoder encodeObject:imageKey forKey:@"imageKey"]; [aCoder encodeInt:valueInDollars forKey:@"valueInDollars"];}
  11. 11. encodeXXX:forKey• 指向对象的指针使⽤用 encodeObject:forKey: 编码• valueInDollars 使⽤用 encodeInt:forKey: 编码,还有 很可以 encode 的类型
  12. 12. • 不管被编码的值是什么类型,总有⼀一个 key 存在• key 是⼀一个⽤用来标识哪个实例变量在被编码的字符串。按照惯例,这个 key 就是被编码的实例变量的名字
  13. 13. 编码对象的递归过程• ⼀一个对象被编码时,被发送 encodeWithCoder: 。 当⼀一个对象被发送 encodeWithCoder:,它⽤用同样 的⽅方式编码它的实例变量 - 也是给它们发送 encodeWithCoder:• 这样编码⼀一个对象就是⼀一个对象编码其他对象的 递归过程
  14. 14. 编码⼀一个对象• 想要被编码,这些对象必须也符合 NSCoding• NSDate, NSString 都是 NSCoding 兼容的
  15. 15. initWithCoder:• encoding 时使⽤用 key 的⺫⽬目的是为了之后在 BNRItem 被从⽂文件系统加载时⽤用来获取已编码的 值• 被从⼀一个 archive 中加载的对象被发送的是 initWithCoder: 消息。• 这个⽅方法攫取所有在 encodeWithCoder: 中被编码 的对象,并且把它们分配给合适的实例变量。• 在 BNRItem.m 中实现这个⽅方法
  16. 16. decodeXXXForKey:- (id)initWithCoder:(NSCoder *)aDecoder{ self = [super init]; if (self) { [self setItemName:[aDecoder decodeObjectForKey:@"itemName"]]; [self setSerialNumber:[aDecoderdecodeObjectForKey:@"serialNumber"]]; [self setImageKey:[aDecoder decodeObjectForKey:@"imageKey"]]; [self setValueInDollars:[aDecoderdecodeIntForKey:@"valueInDollars"]]; dateCreated = [aDecoder decodeObjectForKey:@"dateCreated"]; } return self;} • 这个⽅方法也有⼀一个 NSCoder 参数,它⾥里⾯面的数据 是供正在被初始化的 BNRItem 使⽤用的 • 使⽤用 decodeObjectForKey: 取回对象(object), decodeIntForKey: 取回 valueInDollar
  17. 17. • 经过前⾯面的改造之后,BNRItem 现在是 NSCoding 兼容的了,⽽而且也可以使⽤用 archiving 从⽂文件系统 进⾏行保存和加载。• 现在还有两个问题要考虑: • 我们需要⼀一种⽅方法来开启保存和加载操作?(现在只 是可以 archiving 和 unarchiving) • 另外,我们需要在⽂文件系统找⼀一个地⽅方来存储我们要 保存的 BNRItem
  18. 18. Application Sandbox
  19. 19. • 每⼀一个 iOS 应⽤用都有它⾃自⼰己的 applicaton sandbox(应⽤用沙箱)。• ⼀一个 application sandbox 是在⽂文件系统上的⼀一个 和⽂文件系统其余部分隔离的⺫⽬目录。• 应⽤用必须位于这个 sandbox 内,并且没有其他的 应⽤用可以访问你的 sandbox。
  20. 20. Application Sandbox
  21. 21. application sandbox• Library/Preferences/• tmp/• Documents/• Library/Caches
  22. 22. Library/Preferences/• 所有的⾸首选项都存在这⾥里;“Settings”应⽤用也在这 ⾥里查找应⽤用程序⾸首选项• Library/Preferences 被 NSUserDefaults 类⾃自动化的 进⾏行处理• 当设备和 iTunes 或 iCloud 同步时会被备份
  23. 23. tmp/• ⽤用于写⼊入应⽤用运⾏行时临时⽤用到的数据,⽤用完以后 应该从这个⺫⽬目录删除• 不备份• 使⽤用函数 NSTemporaryDirectory 拿到 application sandbox 中 tmp ⺫⽬目录的路径
  24. 24. Documents• 需要持久化存储的数据可以写到这⾥里• 备份
  25. 25. Library/Caches• 应⽤用程序⽣生成的,还希望能够持久存在• 不会被备份• 不被备份的⼀一个主要的原因就是这些⽂文件可能会 很⼤大,会延⻓长同步设备的时间
  26. 26. 构造⼀一个⽂文件路径• 来⾃自 Homepwner 的 BNRItem 将被保存到 Documents ⺫⽬目录中⼀一个单独的⽂文件中• BNRItemStore 将处理⽂文件写⼊入和读取的⼯工作• ⾸首先,我们需要来构建这个⽂文件的路径• 在 BNRItemStore 中声明和实现⼀一个新的⽅方法
  27. 27. - (NSString *)itemArchivePath{ NSArray *documentDirectories =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES); NSString *documentDirectory = [documentDirectories objectAtIndex:0]; return [documentDirectorystringByAppendingPathComponent:@"items.archive"];} • NSSearchPathForDirectoriesInDomains 函数搜索 ⽂文件系统来查找符合给定参数标准的路径 • 在 iOS 上,最后两个参数总是⼀一样的 • 第⼀一个参数是指定了在 sandbox 中⺫⽬目录的常量。 ⽐比如搜索 NSCachesDirectory 将会返回在应⽤用的 sandbox 中的 Caches ⺫⽬目录 • 返回值是⼀一个字符串数组,在 iOS 上只有⼀一个, 所以只取第⼀一个就可以了
  28. 28. NSKeyedArchiver 和NSKeyedUnarchiver
  29. 29. • 现在我们已经有了⼀一个⽂文件系统的路径,同时模 型对象(BNRItem)也可以被保存到⽂文件系统了 • 我们可以使⽤用 NSKeyedArchiver 在应⽤用 “退出” 时 保存 BNRItems • 在 BNRItemStore.h 中声明⼀一个新的⽅方法:- (BOOL)saveChanges;
  30. 30. archiveRootObject:- (BOOL)saveChanges{ // 返回成功或失败 NSString *path = [self itemArchivePath]; return [NSKeyedArchiver archiveRootObject:allItems toFile:path];} • 在 BNRItemStore.m 中实现这个⽅方法:发送 archiveRootObject:toFile: 消息到 NSKeyedArchiver 类 • archiveRootObject:toFile: ⽅方法负责保存 allItems 中 每个单独的 BNRItem 到 itemArchivePath
  31. 31. • 这个⽅方法会⾸首先创建⼀一个 NSKeyedArchiver 的实 例。• 然后发送 encodeWithCoders: 消息到 root object(这⾥里是 allItems )。• NSKeyedArchiver 是 NSCoder 的⼦子类,所以可以 传给 encodeWithCoder:
  32. 32. • allItems 收到 encodeWithCoder: 消息后,会再发 给它包含的所有的对象,传递的同样是 NSKeyedArchiver。• 这个数组的内容,也就是⼀一堆 BNRItems,再 encode 它们的实例变量到同⼀一个 NSKeyedArchiver。• ⼀一旦所有这些对象都被编码之后, NSKeyedArchiver 就把它收集到的这些数据写到它 的 path 参数(我们前⾯面构建的路径)
  33. 33. Archiving
  34. 34. applicationDidEnterBackGround: • 当⽤用户点击 “Home” 时, applicationDidEnterBackground: 消息被发给 HomepwnerAppDelegate,我们希望在这时候给 BNRItemStore 发送 saveChanges • 我们在 HomepwnerAppDelegate.m 中修改 applicationDidEnterBackGround: 来启动 BNRItems 的保存(别忘了导⼊入 BNRItemStore 头⽂文件)- (void)applicationDidEnterBackground:(UIApplication *)application{ BOOL success = [[BNRItemStore defaultStore] saveChanges]; if (success) { NSLog(@"保存所有的 BNRItem "); } else { NSLog(@"⽆无法保存 BNRItem "); }}
  35. 35. ⽂文件写⼊入确认• 构建并在模拟器中运⾏行,创建⼀一个新的 BNRItems。然后点击 home 键离开应⽤用,检查控 制台,我们应该看到前⾯面代码中记录的⽇日志。• 我们也可以在电脑的⺫⽬目录中确认⼀一下⽂文件是否写 ⼊入成功。在 Finder 中,Command-Shift-G, 键⼊入“~/ Library/Application Support/iPhone Simulator”, 找到应⽤用的 sandbox 查看⼀一下
  36. 36. • 下⼀一步编写加载⽂文件的代码,我们可以在BNRItemStore 被创建时使⽤用 NSKeyedUnarchiver
  37. 37. unarchiveObjectWithFile:- (id)init{ self = [super init]; if (self) {// allItems = [[NSMutableArray alloc] init]; NSString *path = [self itemArchivePath]; allItems = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; // 如果数组之前没有被保存,创建⼀一个新的空数组 if (!allItems) { allItems = [[NSMutableArray alloc] init]; } } return self;}• unarchiveObjectWithFile: ⽅方法创建⼀一个 NSKeyedUnarchiver 实例加载位于 itemArchivePath 的 archive 到它的实例
  38. 38. • 下⾯面我们来看⼀一下加载的过程。• 检测 archive 中 root 对象的类型并且⽣生成这个类型 的实例,在这⾥里,这个类型将是⼀一个 NSMutableArray,因为我们就是使⽤用这个类型的 root object 来⽣生成的这个 archive
  39. 39. • 然后新分配的 NSMutableArray 会被发送 initWithCoder: , NSKeyedUnarchiver 被作为参数传 ⼊入;• 然后就是数组开始从 NSKeyedUnarchiver 解码它 的内容( BNRItem 的实例), 给这些对象发送 initWithCoder: 消息,传递同样的 NSKeyedUnarchiver• 再次构建并运⾏行,测试⼀一下加载功能
  40. 40. 不⽣生成随机数据了 • 因为我们现在已经可以保存和加载 BNRItem 了, 所以我们可以把⽣生成随机数据的功能删除掉。 • 在 BNRItemStore.m 中修改 createItem 的实现让它 ⽣生成⼀一个不带随机数据的空的 BNRItem- (BNRItem *)createItem{// BNRItem *p = [BNRItem randomItem]; BNRItem *p = [[BNRItem alloc] init]; [allItems addObject:p]; return p;}
  41. 41. 应⽤用程序的 States 和 Transitions
  42. 42. • 在 Hompwner 中,我们是在应⽤用程序进⼊入 backgroud state 时 archive BNRItem 的。• 下⾯面我们来看⼀一下应⽤用程序状态有关的内容,包括这 些状态是怎么被触发的,以及我们怎么获得通知• 后⾯面是⼀一个有关状态的概要图
  43. 43. 应⽤用状态
  44. 44. Not running• 不执⾏行任何代码也不占⽤用 RAM 内存
  45. 45. active state• ⽤用户启动⼀一个应⽤用之后,就进⼊入了 active state 。• 应⽤用界⾯面在屏幕上显⽰示,正在接收事件,并且它 的代码在处理这些事件
  46. 46. inactive state• 在 active state 下,应⽤用可以被系统事件临时性的 打断,例如 SMS 消息,推送通知,来电,或者 alarm。应⽤用上⾯面会被叠加⼀一个界⾯面来处理这个事 件。这种状态被称为 inactive state 。
  47. 47. • 在 inactive state 下,应⽤用⼤大部分是可⻅见的,并且 在执⾏行代码,但是没有在接收事件。应⽤用⼀一般在 inactive state 下会停留很短的⼀一个时间。• 可以通过按下设备顶部的锁定按钮(lock)让活 动的应⽤用程序强制进⼊入 inactive state• 直到设备被解锁(unlock)前,应⽤用会⼀一直保持 inactive
  48. 48. background state• 当⽤用户按下 home 键或是某种其他⽅方式切换到其 它应⽤用时,应⽤用进⼊入 background state。• 在 background state 下,界⾯面不可⻅见,也不能接 收消息,但是仍然可以执⾏行代码。• 默认情况下,进⼊入 background state 的应⽤用在进 ⼊入 suspended state 前有 5 秒的时间。
  49. 49. suspended state• 在 suspended state 下的应⽤用不能执⾏行代码,⽆无法 看到它的界⾯面,并且 suspended 时不需要的资源 都会被销毁。
  50. 50. • suspended 的应⽤用处于冻结状态,当⽤用户重新启动它的时候,可以被快速的解冻。• 被销毁的资源都是可以被重新加载的,像缓存的图⽚片,系统管理的缓存,以及其他图⽚片数据。• 我们不需要考虑这些资源的销毁和重新记载,应⽤用程序会⾃自动处理。
  51. 51. 应⽤用状态表 Receives Executes State Visible Events Code Not Running No No No Active Yes Yes Yes Inactive Mostly No Yes Background No No Yes Suspended No No No
  52. 52. background 时保存数据• 应⽤用变更状态时,应⽤用 delegate 会被发送⼀一个消 息。像 application:didFinishLaunchingWithOptions: 等。• 我们可以在这些⽅方法中实现代码采取⼀一些适当的 操作。
  53. 53. • 我们应该在往 background state 转换时保存关键 的修改和应⽤用程序的状态。• 因为这是应⽤用程序在进⼊入 suspended state 前还可 以执⾏行代码的最后时刻。• ⼀一旦进⼊入了 suspended state,应⽤用有可能会被操 作系统根据⾃自⾝身的需要终⽌止。
  54. 54. 使⽤用 NSData 写⼊入⽂文件系统
  55. 55. • 对 Homepwner 进⾏行 archiving 时我们保存和加载 了针对每个 BNRItem 的 imageKey。• 我们可以扩展 image store,在它们被添加时保存 图⽚片,然后在需要的时候可以提取出来。
  56. 56. • BNRItem 实例的 image 是通过⽤用户交互⽣生成的, 并且只存储在应⽤用程序内部。• 这样 Documents ⺫⽬目录就是保存它们的绝好位置。• ⽤用户采集图⽚片时我们⽣生成了⼀一个唯⼀一的 image key,我们可以使⽤用这个 image key 来给⽂文件系统 的⽂文件命名。
  57. 57. 图⽚片路径:imagePathForKey: • 在 BNRImageStore.h 中,添加⼀一个新的⽅方法声明 • 并在 BNRImageStore.m 中实现,可以使⽤用给定的 key 在 documents ⺫⽬目录⽣生成⼀一个路径- (NSString *)imagePathForKey:(NSString *)key{ NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentDirectory = [documentDirectories objectAtIndex:0]; return [documentDirectory stringByAppendingPathComponent:key];}
  58. 58. NSData• 为了保存和加载,我们需要把图⽚片的 JPEG 形式拷 ⻉贝到内存中的 buffer 。• NSData 就可以⽅方便的⽣生成,维护和销毁这些类型 的 buffer。• ⼀一个 NSData 实例持有⼀一定数量字节数的⼆二进制数 据。• 我们可以使⽤用 NSData 存储图⽚片数据。
  59. 59. UIImageJPEGPresentation • 在 BNRImageStore.m 中修改 setImage:forKey: ⽅方法 来获得⼀一个路径,并且保存图⽚片- (void)setImage:(UIImage *)i forKey:(NSString *)s{ [dictionary setObject:i forKey:s]; NSString *imagePath = [self imagePathForKey:s]; NSData *d = UIImageJPEGRepresentation(i, 0.5); [d writeToFile:imagePath atomically:YES];}• UIImageJPEGPresentation 函数的第⼀一个参数是⼀一 个 UIImage 对象,第⼆二个参数是压缩质量,是⼀一个 从 0 -1 的 float,1 是最⾼高质量。• 返回的是 NSData 实例
  60. 60. writeToFile:automatically: NSData *d = UIImageJPEGRepresentation(i, 0.5); [d writeToFile:imagePath atomically:YES];} • 给 NSData 实例发送 writeToFile:automatically: 消 息,NSData 就被写⼊入⽂文件系统。
  61. 61. • automatically 是⼀一个 Boolean 值。• 如果是 YES, ⽂文件会先被写到⽂文件系统的⼀一个临时 位置,⼀一些写⼊入操作完成,这个⽂文件再被使⽤用它 的 path 重命名,已经存在的⽂文件会被替掉。• 设成 automatically 可以防⽌止写⼊入过程中应⽤用崩掉 引起的数据损坏
  62. 62. 同步删除 • 增加完⽂文件以后,再看⼀一下⽂文件的删除。 • 在 BNRImageStore.m 中,我们要确保把⽂文件从 store 中删除的同时,也把它从⽂文件系统中删除。- (void)deleteImageForkey:(NSString *)s{ if (!s) { return; } [dictionary removeObjectForKey:s]; // 确保⽂文件被从 store 删除的同时,也从⽂文件系统删除 NSString *path = [self imagePathForKey:s]; [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];}
  63. 63. imageWithContentsOfFile:• 现在⽂文件已经被存储在⽂文件系统了, BNRImageStore 在被请求的时候需要加载这些⽂文 件。• UIImage 的类⽅方法 imageWithContentsOfFile: 可以 从⽂文件中作为⼀一个 image 读⼊入。• 在 BNRImageStore.m 中替换 imageForKey: , 这样 BNRImageStore 在它没有这个⽂文件的时候就可以 从⽂文件系统进⾏行加载。
  64. 64. imageForKey:// 如果还没有⽂文件的话,从⽂文件系统加载- (UIImage *)imageForKey:(NSString *)s{// return [dictionary objectForKey:s]; // 如果可能的话,就从字典中获取 UIImage *result = [dictionary objectForKey:s]; if (!result) { // 从⽂文件创建⼀一个 UIImage result = [UIImage imageWithContentsOfFile:[self imagePathForKey:s]]; // 如果在⽂文件系统找到⼀一个 image,把它放到 cache 中 if (result) { [dictionary setObject:result forKey:s]; } else { NSLog(@"错误:⽆无法找到 %@", [self imagePathForKey:s]); } } return result;}
  65. 65. • 前⾯面对 image 的增加,删除和记载都完成了,还 有⼀一个问题是 BNRItem 被从 store 中删除时,⽂文件 也应该被从⽂文件系统删除。 • 在 BNRItemStore.m 中,导⼊入 BNRImageStore 的头 ⽂文件,并且添加下列代码到 removeItem:- (void)removeItem:(BNRItem *)p{ // BNRItem 被从 store 中移除的时候,它的 image 也应该被从⽂文件系统删除 NSString *key = [p imageKey]; [[BNRImageStore sharedStore] deleteImageForkey:key]; [allItems removeObjectIdenticalTo:p];}

×