【推荐】【老外写的iOS设计模式系列】第7部分 备忘录模式
2013-09-18 14:43
453 查看
备忘录(Memento)模式
备忘录模式快照对象的内部状态并将其保存到外部。换句话说,它将状态保存到某处,过会你可以不破坏封装的情况下恢复对象的状态,也就是说原来对象中的私有数据仍然是私有的。
如何使用备忘录模式
在ViewController.m中增加下面的方法:
saveCurrentState 保存当前的专辑索引到NSUserDefaults,NSUserDefaults是IOS提供的保存应用设置信息和数据的地方。
loadPreviousState 加载之前保存的索引。这里其实不是备忘录模式完整的实现,但是你已经了解到它了。
现在,在ViewController.m的viewDidLoad方法中,在scroller初始化之前增加下面的代码:
它将在应用启动的时候加载原先保存的状态。但是在什么时候来保存应用的状态呢?你将使用通知来实现它。当应用进入后台的时候,IOS会发送UIApplicationDidEnterBackgroundNotification通知,你可以使用这个通知去保存状态,这是不是很方便?
在viewDidLoad中增加下面的代码:
现在,当应用进入后台的时候,ViewController将通过saveCurrentState方法自动保存当前的状态。
现在增加下面的代码:
这将确保当ViewController被销毁的时候移除观察者。
构建和运行你的应用,导航到一个专辑,然后通过Command+Shift+H(模拟器的情况下)将app发送到后台,然后关闭app。再一次打开app,检查原先选择的专辑是不是被显示在中间:
看起来专辑数据是正确的,但是中间的视图却没有显示正确的专辑。出了什么情况?这是可选方法initialViewIndexForHorizontalScroller的目的所在。因为这个方法没有在委托中实现,这样的话初始化视图总是第一个视图。
为了修正这个问题,在ViewController.m中增加下面的代码:
现在HorizontalScroller的第一个视图终于设置为了currentAlbumIndex指定的视图。这使得app在下次使用的时候还保留了上次使用的状态。
再一次运行你的app,和之前一样滚动专辑,停止应用,重启,确保上面的问题已经修复了:
如果你查看PersistencyManager的init方法,你将注意到专辑数据被硬编码并且每次都要重新创建它们。但是更好的方式是创建专辑列表一次,然后存储它们到一个文件,你怎么保存专辑数据到一个文件呢?
一个可选的方式就是循环Album的属性,保存它们到一个plist文件中,当它们需要的时候再重新构建它们。这个不是一个最好的方式,因为你需要去编写与每个类的属性关联的特定的代码。举例来说如果过会你要创建一个具有不同属性的Movie类,保存和加载的代码需要重新写。
此外,你也不能保存每个类的私有变量,因为它们在外面的类中是不可见的。这正是苹果创建了归档(Archiving)机制的原因。(译者注:Java中这里也可以说是序列化)
归档(Archiving)
归档是苹果对于备忘录模式的特定实现之一。这种机制可以转换一个对象到一个可保存的数据流中,过会可以在不暴漏私有属性给外部的情况下重建它们。你可以在iOS 6 by Tutorials书的第16章读取更多关于此功能的信息,或者你也可以参考:Apple’s
Archives and Serializations Programming Guide.
如何使用归档
首先,你需要声明Album可以被归档的,这需要Album遵循NSCoding协议。打开Album.h文件,改变@interface行为如下所示:
在Album.m中增加如下的两个方法:
你可以在归档一个类的实例对象的时候调用encodeWithCoder:,相反的当你要从归档中重建Album实例的时候,你可以调用initWithCoder:,这样做是不是很简单,但是它是一种强大的机制哦。
在PersistencyManager.h中,增加下面的签名(方法原型):
这个正是保存专辑的方法。
现在在PersistencyManager.m中,增加方法的实现:
NSKeyedArchiver归档专辑数据到albums.bin文件中。
当你在归档一个对象的时候,归档器会递归的归档对象包含的子对象以及子对象的子对象等等。在本例中,归档开始自一个名为albums的数组,因为NSArry和Album两者都支持NSCoding协议,因此数组中每个对象都会被归档.
现在用下面的代码取代PersistencyManager.m中的init方法:
新的代码中,如果专辑数据存在,NSKeyedUnarchiver会从文件中加载专辑数据,如果专辑数据不存在,它会创建专辑数据并立即保存它以便下次启动的时候使用。
你想在每次app进入后台的时候都保存专辑数据。现在这可能看起来不是很必要,但是如果过会你想增加一个修改专辑数据的选项呢?那时候你就想确保所有的改变都会被保存。
在Library.h中增加下面的代码:
因为主应用通过LibraryAPI访问所有的服务,这样就要求PersistencyManager知道它负责保存专辑数据。
现在在LibraryAPI.m实现中增加方法实现:
这个方法将调用LibraryAPI保存数据的请求委托给PersistencyManager处理。在ViewController.m中saveCurrentState方法末尾,增加如下的代码:
无论何时ViewController保存应用状态的时候,上面的代码使用LibraryAPI触发专辑数据的保存。
构建你的应用,检查每个资源是否被正确编译。
不幸的是,没有一个简单的方式去检查数据持久化的正确性。你可以通过Finder在应用的Documents目录查看到专辑数据文件已经被创建,但是为了能看到任何其它的变化,你还需要增加改变专辑数据的功能。
但是并不仅仅是改变数据,如果你需要删除不想要的专辑数据呢?另外,是不是可以很漂亮的来增加一个撤销删除的功能呢?
这就到了我们讨论下个设计模式(命令模式)的机会了。
原文出处:http://xmuzyq.iteye.com/blog/1942386
备忘录模式快照对象的内部状态并将其保存到外部。换句话说,它将状态保存到某处,过会你可以不破坏封装的情况下恢复对象的状态,也就是说原来对象中的私有数据仍然是私有的。
如何使用备忘录模式
在ViewController.m中增加下面的方法:
- (void)saveCurrentState { // When the user leaves the app and then comes back again, he wants it to be in the exact same state // he left it. In order to do this we need to save the currently displayed album. // Since it's only one piece of information we can use NSUserDefaults. [[NSUserDefaults standardUserDefaults] setInteger:currentAlbumIndex forKey:@"currentAlbumIndex"]; } - (void)loadPreviousState { currentAlbumIndex = [[NSUserDefaults standardUserDefaults] integerForKey:@"currentAlbumIndex"]; [self showDataForAlbumAtIndex:currentAlbumIndex]; }
saveCurrentState 保存当前的专辑索引到NSUserDefaults,NSUserDefaults是IOS提供的保存应用设置信息和数据的地方。
loadPreviousState 加载之前保存的索引。这里其实不是备忘录模式完整的实现,但是你已经了解到它了。
现在,在ViewController.m的viewDidLoad方法中,在scroller初始化之前增加下面的代码:
[self loadPreviousState];
它将在应用启动的时候加载原先保存的状态。但是在什么时候来保存应用的状态呢?你将使用通知来实现它。当应用进入后台的时候,IOS会发送UIApplicationDidEnterBackgroundNotification通知,你可以使用这个通知去保存状态,这是不是很方便?
在viewDidLoad中增加下面的代码:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveCurrentState) name:UIApplicationDidEnterBackgroundNotification object:nil];
现在,当应用进入后台的时候,ViewController将通过saveCurrentState方法自动保存当前的状态。
现在增加下面的代码:
- (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }
这将确保当ViewController被销毁的时候移除观察者。
构建和运行你的应用,导航到一个专辑,然后通过Command+Shift+H(模拟器的情况下)将app发送到后台,然后关闭app。再一次打开app,检查原先选择的专辑是不是被显示在中间:
看起来专辑数据是正确的,但是中间的视图却没有显示正确的专辑。出了什么情况?这是可选方法initialViewIndexForHorizontalScroller的目的所在。因为这个方法没有在委托中实现,这样的话初始化视图总是第一个视图。
为了修正这个问题,在ViewController.m中增加下面的代码:
- (NSInteger)initialViewIndexForHorizontalScroller:(HorizontalScroller *)scroller { return currentAlbumIndex; }
现在HorizontalScroller的第一个视图终于设置为了currentAlbumIndex指定的视图。这使得app在下次使用的时候还保留了上次使用的状态。
再一次运行你的app,和之前一样滚动专辑,停止应用,重启,确保上面的问题已经修复了:
如果你查看PersistencyManager的init方法,你将注意到专辑数据被硬编码并且每次都要重新创建它们。但是更好的方式是创建专辑列表一次,然后存储它们到一个文件,你怎么保存专辑数据到一个文件呢?
一个可选的方式就是循环Album的属性,保存它们到一个plist文件中,当它们需要的时候再重新构建它们。这个不是一个最好的方式,因为你需要去编写与每个类的属性关联的特定的代码。举例来说如果过会你要创建一个具有不同属性的Movie类,保存和加载的代码需要重新写。
此外,你也不能保存每个类的私有变量,因为它们在外面的类中是不可见的。这正是苹果创建了归档(Archiving)机制的原因。(译者注:Java中这里也可以说是序列化)
归档(Archiving)
归档是苹果对于备忘录模式的特定实现之一。这种机制可以转换一个对象到一个可保存的数据流中,过会可以在不暴漏私有属性给外部的情况下重建它们。你可以在iOS 6 by Tutorials书的第16章读取更多关于此功能的信息,或者你也可以参考:Apple’s
Archives and Serializations Programming Guide.
如何使用归档
首先,你需要声明Album可以被归档的,这需要Album遵循NSCoding协议。打开Album.h文件,改变@interface行为如下所示:
@interfaceAlbum : NSObject<NSCoding>
在Album.m中增加如下的两个方法:
- (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.year forKey:@"year"]; [aCoder encodeObject:self.title forKey:@"album"]; [aCoder encodeObject:self.artist forKey:@"artist"]; [aCoder encodeObject:self.coverUrl forKey:@"cover_url"]; [aCoder encodeObject:self.genre forKey:@"genre"]; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; if (self) { _year = [aDecoder decodeObjectForKey:@"year"]; _title = [aDecoder decodeObjectForKey:@"album"]; _artist = [aDecoder decodeObjectForKey:@"artist"]; _coverUrl = [aDecoder decodeObjectForKey:@"cover_url"]; _genre = [aDecoder decodeObjectForKey:@"genre"]; } return self; }
你可以在归档一个类的实例对象的时候调用encodeWithCoder:,相反的当你要从归档中重建Album实例的时候,你可以调用initWithCoder:,这样做是不是很简单,但是它是一种强大的机制哦。
在PersistencyManager.h中,增加下面的签名(方法原型):
- (void)saveAlbums;
这个正是保存专辑的方法。
现在在PersistencyManager.m中,增加方法的实现:
- (void)saveAlbums { NSString *filename = [NSHomeDirectory() stringByAppendingString:@"/Documents/albums.bin"]; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:albums]; [data writeToFile:filename atomically:YES]; }
NSKeyedArchiver归档专辑数据到albums.bin文件中。
当你在归档一个对象的时候,归档器会递归的归档对象包含的子对象以及子对象的子对象等等。在本例中,归档开始自一个名为albums的数组,因为NSArry和Album两者都支持NSCoding协议,因此数组中每个对象都会被归档.
现在用下面的代码取代PersistencyManager.m中的init方法:
- (id)init { self = [super init]; if (self) { NSData *data = [NSData dataWithContentsOfFile:[NSHomeDirectory() stringByAppendingString:@"/Documents/albums.bin"]]; albums = [NSKeyedUnarchiver unarchiveObjectWithData:data]; if (albums == nil) { albums = [NSMutableArrayarrayWithArray: @[[[Album alloc] initWithTitle:@"Best of Bowie" artist:@"David Bowie" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_david%20bowie_best%20of%20bowie.png" year:@"1992"], [[Album alloc] initWithTitle:@"It's My Life" artist:@"No Doubt" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_no%20doubt_its%20my%20life%20%20bathwater.png" year:@"2003"], [[Album alloc] initWithTitle:@"Nothing Like The Sun" artist:@"Sting" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_sting_nothing%20like%20the%20sun.png" year:@"1999"], [[Album alloc] initWithTitle:@"Staring at the Sun" artist:@"U2" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_u2_staring%20at%20the%20sun.png" year:@"2000"], [[Album alloc] initWithTitle:@"American Pie" artist:@"Madonna" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png" year:@"2000"]]]; [self saveAlbums]; } } return self; }
新的代码中,如果专辑数据存在,NSKeyedUnarchiver会从文件中加载专辑数据,如果专辑数据不存在,它会创建专辑数据并立即保存它以便下次启动的时候使用。
你想在每次app进入后台的时候都保存专辑数据。现在这可能看起来不是很必要,但是如果过会你想增加一个修改专辑数据的选项呢?那时候你就想确保所有的改变都会被保存。
在Library.h中增加下面的代码:
- (void)saveAlbums;
因为主应用通过LibraryAPI访问所有的服务,这样就要求PersistencyManager知道它负责保存专辑数据。
现在在LibraryAPI.m实现中增加方法实现:
- (void)saveAlbums { [persistencyManager saveAlbums]; }
这个方法将调用LibraryAPI保存数据的请求委托给PersistencyManager处理。在ViewController.m中saveCurrentState方法末尾,增加如下的代码:
[[LibraryAPI sharedInstance] saveAlbums];
无论何时ViewController保存应用状态的时候,上面的代码使用LibraryAPI触发专辑数据的保存。
构建你的应用,检查每个资源是否被正确编译。
不幸的是,没有一个简单的方式去检查数据持久化的正确性。你可以通过Finder在应用的Documents目录查看到专辑数据文件已经被创建,但是为了能看到任何其它的变化,你还需要增加改变专辑数据的功能。
但是并不仅仅是改变数据,如果你需要删除不想要的专辑数据呢?另外,是不是可以很漂亮的来增加一个撤销删除的功能呢?
这就到了我们讨论下个设计模式(命令模式)的机会了。
原文出处:http://xmuzyq.iteye.com/blog/1942386
相关文章推荐
- 【推荐】【老外写的iOS设计模式系列】第5部分 适配器模式
- 【推荐】【老外写的iOS设计模式系列】第6部分 观察者模式
- 【推荐】【老外写的iOS设计模式系列】第3部分 门面模式
- 【推荐】【老外写的iOS设计模式系列】第4部分 装饰器模式
- 【推荐】【老外写的iOS设计模式系列】第8部分 命令模式
- 【推荐】【老外写的iOS设计模式系列】第1部分
- 【推荐】【老外写的iOS设计模式系列】第2部分 MVC模式&单例模式
- 极速理解设计模式系列:19.备忘录模式(Memento Pattern) 推荐
- 设计模式系列-工厂模式 推荐
- 翻转设计模式系列——第三部分--设计模式(1)--概览
- iOS 设计模式系列:开篇
- 设计模式系列-原型模式 推荐
- iOS 设计模式系列:Singleton – 单例模式
- Android设计模式系列(5)--SDK源码之备忘录模式
- Android设计模式系列(5)--SDK源码之备忘录模式
- PHP设计模式系列(十三):备忘录模式
- iOS设计模式(代码分析系列2:简单工厂模式)
- 设计模式系列 - 简单工厂 推荐
- iOS开发那些事-iOS常用设计模式–委托模式案例实现 推荐
- [推荐] 常见设计模式的解析和实现(C++) 全20部分