標籤:objecitve-c cocoa 歸檔 mvc
前面的博文裡介紹了歸檔和解檔,這裡我們把它實際應用到一個簡單的代碼中去,將它作為一個多文檔應用程式的開啟和儲存的背後支援。另外這裡介紹一下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
程式執行介面如下:
我們還可以設定私人封存檔案的表徵圖以及副檔名,如: