GNUstep 支援 non-blocking I/O, 以避免在存取檔案或是網路時, 使用者介面無法被操作. 同時也免去使用多執行緒的麻煩. 在這個製做一個程式, 使用 Unix 的 find 指令來搜尋檔案. GNUstep-examples 中的 Finger 亦是個很好的參考, 本範例即以 Finger 為範本.
使用 Gorm 製做以下使用者介面, 可以改變自動調整大小的屬性以達到最好的效果.
繼承自 NSObject, 產生一個 Controller 類別及物件. 加入 "label" 和 "textView" outlets, 並連結上使用者介面. 加入 "searchAction:" action, 並將 NSTextField 連結到這個 action. 指定 Controller 為 NSOwner (NSApp) 的代理者, 存檔.
產生 Controller 的程式碼.
當使用者在 NSTextField 中按下 ENTER 鍵時, 會呼叫 -searchAction:. 因此在 -searchAction: 中使用 NSTask 來呼叫 find 來搜尋檔案. GNUstep 使用 NSTask 來執行一般 Unix 的指令, 並使用訊息傳回其結果, 以避免影響使用者操作. 以下是 -searchAction: 的程式碼:
Controller.m
- (void) searchAction: (id) sender { NSString *file; NSArray *args; NSTask *task; file = [sender stringValue]; args = [NSArray arrayWithObjects: NSHomeDirectory(), @"-name", file, @"-print", nil]; ASSIGN(pipe, [NSPipe pipe]); task = [NSTask new]; [task setLaunchPath: @"/usr/bin/find"]; [task setArguments: args]; [task setStandardOutput: pipe]; fileHandle = [pipe fileHandleForReading]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(taskEnded:) name: NSTaskDidTerminateNotification object: nil]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(readData:) name: NSFileHandleReadCompletionNotification object: fileHandle]; [fileHandle readInBackgroundAndNotify]; [task launch]; }
在 NSTask 中設定好 Unix 指令的路徑及要傳入的參與. 將 NSTask 輸出導向 NSPipe, 再從 NSPipe 中取得 NSFileHandle, 如此便可以通過 NSFileHandle 取得 NSTask 的輸出. 接下來將 Controller 指定為訊息的接收者, 當 NSFileHandle 讀取完畢及 NSTask 結束時會收到相關訊息.
最後, 將 NSFileHandle 在背景中啟動, 並啟動 NSTask. 這是 NSFileHandle 及 NSTask 都不會干擾使用者介面的操作.
最後, 要處理相關訊息的傳入.
Controller.m
- (void) readData: (NSNotification *) not { NSData *data = [[not userInfo] objectForKey: NSFileHandleNotificationDataItem] ; NSString *string = [[NSString alloc] initWithData: data encoding: [NSString defaultCStringEncoding]]; [textView setString: string]; } - (void) taskEnded: (NSNotification *) not { [[NSNotificationCenter defaultCenter] removeObserver: self]; [fileHandle closeFile]; }
當 NSTask 的結果全都出來了, 會通知 -readData:, 這是在 -searchAction: 中指定好的. 這時從 NSFileHandle 中取得其結果, 並放到 NSTextView 上. 當 NSTask 結束時, 將 Controller 從訊息中心中移除, 並關閉 NSFileHandle.
有關訊息的名稱及可用的關鍵字, 可以參考 NSFileHandle 及 NSTask 的檔頭. 程式碼在此: Search-src.tar.gz
NSFileHandle 除了可讀寫檔案之外, 亦可透過 BSD socket 來讀寫網路資料. 但是 OpenSTEP 中沒有產生 socket 的程式介面, 必需自行使用 C 程式庫來撰寫. GNUstep 提供了延伸功能來讀寫網路資料, 這部份可自行參考 NSFileHandle 的檔頭.
除了 NSFileHandle, NSURL 及 NSURLHandle 亦提供 non-blocking I/O 的方式來讀取特定 URL 的資料. 在 NSURL 中, 先製做一個接收訊息的物件 (client object), 再使用 -loadResourceDataNotifyingClient:usingCache: 在背景讀取資料. 該物件 (client object) 會自動收到訊息. 在 NSURLHandle, 也是先製做一個物件來接收訊息, 然後用 -loadInBackground 在背景取得 URL 資料. 這些大概是需要 non-blocking I/O 類別. 如果想要自行製做一個 non-blocking I/O 類別, 那要自行研究 GNUstep 的 run loop. 這就超過本書的範圍了.
這裡有一個 netclasses 類別, 專門用來處理網路介面的溝通, 適合做為伺服器程式的網路介面. 數個線上交談軟體都使用 netclasses 來處理網路的溝通. 不想使用 BSD socket 的人可以試試.