存取檔案

文件程式最重要的就是能存取檔案. 根據檔案的複雜度, 有數種 methods 可以實作.

表格 13.1. 存取檔案的 Methods

 讀取檔案儲存檔案
高階 (簡單)loadDataRepresentation:ofType:dataRepresentationOfType:
中階loadFileWrapperRepresentation:ofType:fileWrapperRepresentationOfType:
低階readFromFile:ofType:, readFromURL:ofType:writeToFile:ofType:, writeToURL:ofType:

可參考 How do I implement saving and loading for simple files?, How do I implement document packages (documents that are really folders, but appear to be opaque documents)?, 以及 How do I implement loading and saving when the simple data or file wrapper API won't do?.

在這個簡單的例子中, 使用高階的 -loadDataRepresentation:ofType: and -dataRepresentationOfType: 便足夠了.

Document.m:

- (NSData *) dataRepresentationOfType: (NSString *) type
{
   if (type == nil)
      type = @"mon";
   if ([type isEqualToString: @"mon"])
      return [NSArchiver archivedDataWithRootObject: records];
   else
      return nil;
}

- (BOOL) loadDataRepresentation: (NSData *) data ofType: (NSString *) type
{
   if ([type isEqualToString: @"mon"])
      {
         [records setArray: [NSUnarchiver unarchiveObjectWithData: data]];
         return YES;
      }
   return NO;
}

當使用者要存檔時, 會呼叫 -dataRepresentationOfType:. type 即是程式屬性檔 (MoneyInfo.plist) 中的 NSName. 在這裡使用 NSArchiver 將整個物件轉成 NSData, 至於其內部結構則不用理會. 當使用者要開啟檔案時, 會呼收 -loadDataRepresentation:ofType:. 這時使用 NSUnarchiver 將整個 NSData 轉換回 NSArray. 如此便完成了讀存檔的功能. 如果想將檔案存成特定格式, 那就必需自行處理資料結構的問題. 可參考 data archivesUsing the Property List Objects and the NSCoding Protocol.

使用 NSArchiver 存成的檔案是位元檔, 而不是文字檔. 因此不能使用一般的文字編輯器來修改, 在資料的交換上其實不太方便. 因為 NSArray 及 NSDictionary 都是簡單的資料結構, 可以轉換成屬性檔 (property list), 這時就是一般可處理的文字檔格式了.

Document.m:

- (NSData *) dataRepresentationOfType: (NSString *) type
{
   if (type == nil)
      type = @"mon";
   if ([type isEqualToString: @"mon"])
      return [[records description] dataUsingEncoding: [NSString defaultCStringEncoding]];
   else
      return nil;
}

- (BOOL) loadDataRepresentation: (NSData *) data ofType: (NSString *) type
{
   if ([type isEqualToString: @"mon"])
      {
        NSString *string = [[NSString alloc] initWithData: data 
                                                 encoding: [NSString defaultCStringEncoding]];
        [records setArray: [string propertyList]];
        RELEASE(string);
        return YES;
      }
   return NO;
}

在這裡使用 -description 將資料結構轉成屬性檔, 其為一字串 (文字格式). 再將其字串轉成 NSData 即可. 反之用於讀檔. 如此便可將資料存成一般的文字檔. 只有簡單的資料結構可以存成屬性檔. 像是 NSCalendarDate, NSHost 等便不能存成屬字檔, 只能用 NSArchiver 轉成位元檔, 或是自行想辦法將其轉成文字檔.

程式碼在此: LoadSave-src.tar.gz.

如果要將文件存成一個目錄, 例如一個 HTML 文件包含了文字及圖檔, 都要放在同一個目錄, 可以使用 -loadFileWrapperRepresentation:ofType: and -fileWrapperRepresentationOfType:. -readFromFile:ofType: and -writeToFile:ofType: 則可直接接觸檔案系統, 可與 NSFileHandle 並用.