前面的博文里介绍了归档和解档,这里我们把它实际应用到一个简单的代码中去,将它作为一个多文档应用程序的打开和保存的背后支持。另外这里介绍一下MVC思想,这个在任何语言里都会有,它是一种设计思想,主要可以概括为一个程序由3部分组成:
1 模式:是程序的数据支持;
2 视图:是程序的表示支持;
3 控制:连接模式和视图,将程序构为一个整体;
Cocoa框架中对MVC提供了非常好的支持,你只需要写很少的代码就可以完成一个程序的MVC绑定了。下面的例子中,我生成一个基于多文档的程序,使用了NSArrayController类作为控制器,它的数据源为NSArray,其中每个元素由我定义的类Person来描述;主窗口中的tab_view作为主类中的outlets,主类为Document,它派生自NSDocument用来支持多文档中的每一个文档窗口。
添加add和remove按钮,都与NSArrayController中的add和remove方法绑定;tab_view控件的两列分别与Person类中的2个属性绑定,每一行自然是Person数组中的每一个Person对象了。这样每个视图中的数据表示(tab_view)通过控制器与模式相连,视图内容的改变(通过add和remove按钮)也通过控制器从而导致模式数据的改变;而模式自身的改变(通过读档操作)也会更新视图的显示哦。这样保证了视图和模式的独立性:模式可以在其他视图上显示,而视图也可以绑定其他的模式。
最后,利用归档化实现了程序的save和open功能,也基本没写几行代码,而且save后的文件也自动与我们的程序绑定起来,如果双击该文件,会自动用我们的app打开哦,真是十分的方便。具体请看代码:
// // Document.h // mac_doc // // Created by kinds on 14-7-7. // Copyright (c) 2014年 kinds. All rights reserved. // #import <Cocoa/Cocoa.h> @interface Document : NSDocument{ IBOutlet NSTableView *tab_view; NSMutableArray *persons; } -(void)setPersons:(NSMutableArray *)ary; @end
// // Document.m // mac_doc // // Created by kinds on 14-7-7. // Copyright (c) 2014年 kinds. All rights reserved. // #import "Document.h" @interface Document () @end @implementation Document - (instancetype)init { self = [super init]; if (self) { // Add your subclass-specific initialization here. persons = [[NSMutableArray alloc]init]; } return self; } -(void)setPersons:(NSMutableArray *)ary{ if(ary == persons) return; persons = ary; } - (void)windowControllerDidLoadNib:(NSWindowController *)aController { [super windowControllerDidLoadNib:aController]; // Add any code here that needs to be executed once the windowController has loaded the document's window. } + (BOOL)autosavesInPlace { return YES; } - (NSString *)windowNibName { // Override returning the nib file name of the document // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead. return @"Document"; } - (NSData *)dataOfType:(NSString *)name error:(NSError **)out_err { // Insert code here to write your document to data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning nil. // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead. [[tab_view window] endEditingFor:nil]; return [NSKeyedArchiver archivedDataWithRootObject:persons]; } - (BOOL)readFromData:(NSData *)data ofType:(NSString *)name \ error:(NSError **)out_err { // Insert code here to read your document from the given data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning NO. // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead. // If you override either of these, you should also override -isEntireFileLoaded to return NO if the contents are lazily loaded. NSMutableArray *new_ary = nil; @try{ new_ary = [NSKeyedUnarchiver unarchiveObjectWithData:data]; }@catch(NSException *e){ NSLog(@"exception = %@",e); if(out_err){ NSDictionary *d = [NSDictionary dictionaryWithObject:\ @"data is corrupted!" \ forKey:NSLocalizedFailureReasonErrorKey]; *out_err = [NSError errorWithDomain:NSOSStatusErrorDomain \ code:unimpErr userInfo:d]; } return NO; } [self setPersons:new_ary]; return YES; } @end
// // Person.h // mac_doc // // Created by kinds on 14-7-7. // Copyright (c) 2014年 kinds. All rights reserved. // #import <Foundation/Foundation.h> @interface Person : NSObject <NSCoding>{ NSString *name; float exp_raise; } @property(readwrite,copy)NSString *name; @property(readwrite)float exp_raise; @end
// // Person.m // mac_doc // // Created by kinds on 14-7-7. // Copyright (c) 2014年 kinds. All rights reserved. // #import "Person.h" @implementation Person @synthesize name,exp_raise; -(id)initWithCoder:(NSCoder *)coder{ self = [super init]; if(self){ name = [coder decodeObjectForKey:@"name"]; exp_raise = [coder decodeFloatForKey:@"exp_raise"]; } return self; } -(void)encodeWithCoder:(NSCoder *)coder{ [coder encodeObject:name forKey:@"name"]; [coder encodeFloat:exp_raise forKey:@"exp_raise"]; } -(id)init{ self = [super init]; if(self){ exp_raise = 0.05; name = @"no_name"; } return self; } -(void)setNilValueForKey:(NSString *)key{ if([key isEqualToString:@"exp_raise"]) self.exp_raise = 0.0; else [super setNilValueForKey:key]; } @end
程序执行界面如下:
我们还可以设置私有存档文件的图标以及扩展名,如下图: