WebObjects/Web 應用/開發/Cocoa EO 應用
注意:這些技術依賴於已棄用的技術。使用需自擔風險。-- Mike Schrag
CocoaEOApplication(在 WO 5.2 中正式稱為 Cocoa 企業物件應用程式,但此處簡稱為 CEO)是一組物件,允許您在 Cocoa 中使用企業物件框架 (EOF)。即使這項技術尚未得到 Apple 的支援,它也自 NeXT 獨立以來就一直存在。
在其早期版本中,CEO 使用 Objectve-C 作為其主要語言(現在是 Cocoa 的主要語言),但自 WO 5.0 以來,使用 EOF 的主要語言是 Java。為了在 Cocoa 中使用 EOF(特別是為了在 OSX 中使用 EOModeler),Apple 開發了所謂的 Java Bridge(例如,參見http://cocoadevcentral.com/articles/000024.php --- 有一篇名為“使用 Java Bridge”的文件,但在撰寫本文時(2003 年 7 月 24 日),它在 developer.apple.com 上已不再可用;但是您可能會在硬碟上的 /Developer/Documentation/JavaBridge/JavaBridge.pdf 中找到一份副本)。為了開發 CEO,您必須習慣其機制。
更新(2005 年 6 月 3 日):JavaBridge.pdf 現在位於http://developer.apple.com/documentation/Cocoa/Conceptual/Legacy/JavaBridge/JavaBridge.pdf
在建立 CEO 專案時,有一些事項需要注意(此註釋適用於 WO 5.2,並已針對 5.2.2 進行修訂,現在適用於 WO 5.2.4)
當您啟動 Project Builder 的嚮導並請求 Cocoa/EO 應用程式時,它將建立一個不可用的 MainMenu.nib 檔案。要解決此問題:關閉專案;刪除“損壞”的檔案 MainMenu.nib(它位於專案的 English.lproj 資料夾中);啟動 Interface Builder 並建立一個空介面;將其另存為 MainMenu.nib(最好使用副檔名)到“損壞”檔案所在的位置;重新啟動 Project Builder;完成。
- .... 在 OSX 10.3(Panther+Xcode)的 WO 5.2.2 中,已修復了之前的錯誤。
- .... 在 OSX 10.4(Tiger+Xcode)的 WO 5.2.4 中,它仍然有效。
第一次從 EOModeler 將實體拖到 Interface Builder 中時,將為您建立一個 EOEditingContext(如果將實體拖到視窗上,還將建立一個 NSTableView 和一個 EODisplayGroup;如果拖到類檢視器上,則只會建立 EODisplayGroup)。預設情況下(並非總是理想)是在載入時獲取實體中的所有物件;如果您不希望這樣做,請轉到檢查器(選擇 EODisplayGroup),然後取消選中相應的按鈕。
基本上,您已經準備就緒。您可以使用顯示組和編輯上下文的所有操作來檢視和操作您的資料庫,而無需編寫任何程式碼 --- 事實上,您既不必編譯專案;只需連線您的按鈕並從檔案選單執行“測試介面”命令(或從鍵盤使用 cmd+r),您就會看到您的資料庫顯示出來。
當然,如果您是一位經驗豐富的程式設計師並希望向您的應用程式新增一些“邏輯”,您可以這樣做。您需要做的第一件事是選擇一種語言;我更喜歡 Obj-C,但也可以使用 Java... 事實上,即使您決定不使用它,它也會被使用。EOF 是用純 Java 編寫的,因此,即使您沒有用 Java 編寫一行程式碼(有時這似乎是不可避免的),您也會透過 Java Bridge 使用它。我稍後會詳細介紹...
根據一位虛擬朋友(Arturo Perez)的建議,我決定新增一個簡單的指南來構建 CEO。
這真的很簡單... 幾乎和 D2W 一樣簡單,但更美觀。
有模型嗎?如果有,您可以操作您的資料庫,而無需編寫任何程式碼。嘗試以下操作
- 建立一個 Cocoa/EO 專案並匯入您的模型(如果您仍在使用 5.2.1,請注意重新建立 nib 檔案)
- 開啟您的 MainMenu.nib(來自 PB 或 Xcode)並將一個新視窗從 IB 的調色盤拖到其中
- 開啟您的模型並將一個實體拖到視窗中
- 在 IB 中,按 cmd+r
您的表格將顯示出來!!
如果您熟悉 IB,剩下的就是歷史了,但如果您不熟悉,可以嘗試
- 將一個按鈕從調色盤拖到您的視窗中
- 將其(使用 cntrl+拖動)連線到您的顯示組(在步驟 3 中建立)並透過雙擊選擇插座“插入”
- 按 cmd+r
- 按下按鈕
您將在表格中看到一個新行!!
您想新增一些自定義邏輯嗎?
9. 選擇“類”選項卡,選擇您的根類(我更喜歡 NSObject)並按 Enter 10. 按 cmd+1,新增一個插座(displayGroup)和一個動作(doit:) 11. 按 cmd+opt+f(對面板回答“確定”)並按 cmd+opt+i 12. 連線您的插座,新增一個按鈕並將其連線到您的動作。儲存。13. 在 PB(或 Xcode)中,您將看到您的檔案。編輯它們以實現 doit:。例如,嘗試以下內容
- (IBAction)doit:(id)sender{
[[displayGroup displayedObjects] takeValueForKey:aValue :@"aKey"];
}
- 在 PB(或 Xcode)中按 cmd+r
您將編譯並執行您的新應用程式。表格顯示後,按下(第二個)按鈕。
就是這樣!!您現在可以準備開發市場上最好的應用程式了 ;^()
如果您知道您將需要大量的記憶體,或者在控制檯中收到“java.lang.outOfMemoryError”訊息,或者您的應用程式根本沒有執行其應執行的操作,您可能需要閱讀本節... 否則,只需跳過它。
您還在嗎?好吧,您的問題可能是 VJM 沒有為您分配足夠的記憶體。
在 Project Builder 中,在目標下並選擇主目標,轉到 Info.plist 條目 > 純 Java 特定(在 Xcode 中,您必須單擊目標組的小三角形,雙擊主目標)。在“附加 VM 選項”文字欄位中新增類似以下內容
-Xms256m -Xmx256m
(這將為您的 JVM 提供 256MB 的記憶體,而不是預設的 64MB。)
此外,習慣正確使用 NSAutoreleasePool 也很方便(請參閱其文件和相關主題;特別是閱讀“Objective-C 程式語言”一書的第 4 章http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC)。Java Bridge 在 Java 端很好地使用了“垃圾回收”,在 Obj-C 端使用了“引用計數”;不用擔心。
正如我所說,即使您的程式碼僅使用 Obj-C,您也將在透過 Bridge 處理 Java 物件;因此,您必須知道如何在 Java 端操作這些“分配”的物件。
從 Obj-C 端在 Java 端分配物件的最簡單方法是,例如,
id aJavaObject = [NSClassFromString(@"java.lang.Object") new];
不要忘記釋放此類物件;因為您分配了它,所以您擁有它!
對於來自 com.apple.cocoa.foundation.* 的那些物件,您實際上不必關心(除了 NSEnumerator 之外);它們來回傳遞都很正常。甚至一些簡單的物件,如 String(對映到 NSString)、Number(對映到 NSNumber)以及一些基本型別,如 int、char、boolean 等。
到目前為止,一切順利。但是,當您想要列舉陣列時要小心。一方面,如果陣列是在 Obj-C 端建立的,並且您請求一個 objectEnumerator,您將收到一個 NSEnumerator。此物件實現 nextObject 方法來遍歷陣列的元素。另一方面,如果陣列是在 Java 端建立的(例如,作為 allObjects() 呼叫的結果),並且您請求一個 objectEnumerator,您將收到一個實現 nextElement() 方法的 Enumerator 物件。
以下是一些這兩種情況的示例。
一個 Obj-C 陣列
NSArray* anObjCArray = [NSArray arrayWithObjects:objA, objB, objC, objD, nil];
NSEnumerator* en = [anObjCArray objectEnumerator];
id o = nil;
while(o = [en nextObject]){
// here your code using o,
// which will traverse all objects in the array
}
一個 Java 陣列
NSArray* aJavaArray = [someDisplayGroup displayedObjects];
NSEnumerator* en = [aJavaArray objectEnumerator];
while([en hasMoreObjects]){
id o = [en nextElement];
// here your code using o, which will traverse all objects in the array
}
這些簡單的示例展示了在開發 CEO 時需要牢記的一些事項
- Obj-C 的方法可以接受任意長度的物件列表作為引數(通常以 nil 結尾);Java 的方法則不行。如果你知道你的方法將由 Java 物件處理,**千萬不要**使用這種結構……它根本不會被識別。相反,Java 使用以下形式的陣列。
new Object[] {objA,objB,objC,objD}
因此,你必須**首先**建立陣列,並在分配後將其用作引數。一個重要的例子是類方法 qualifierWithQualifierFromat(String format, NSArray arguments),它由 com.webobjects.eocontrol.EOQualifier 實現。我稍後會再討論這個問題……
- NSEnumerator 以一個 nil 物件結束其“路徑”;如果 Java 的 Enumerator 被請求獲取更多元素,則會丟擲異常。因此,為了避免這種異常,你必須使用 hasMoreElements 方法。
- Java 端的物件從 Obj-C 端呼叫,就像它們是 Obj-C 物件一樣(使用相同的語法);也就是說,如果 Java 物件 anObject 實現了方法 someMethod(Object someParameter, Object someOther),則必須使用以下形式的程式碼呼叫它:
[anObject someMethod:someParameter :someOther];
也許 CEO 整合中最糟糕的部分是陣列的管理;有 5 種類型的陣列你必須注意
- NSArray;如 Foundation.h 中定義(Obj-C 端)。
- NSArray;如 com.apple.cocoa.foundation 中定義(Java 端)。
- NSArray;如 com.webobjects.foundation 中定義(Java 端)。
- 物件的 C 陣列(如 id array[])。
- 物件的 Java 陣列(如 Object[])。
這太多了!但是生活就是生活,最好適應它們……
通常,當你不需要在 Cocoa 中使用 EOF 時,你可以忘記 com.webobjects.foundation 中的陣列;橋接器預設情況下會將 Foundation.h 中的陣列轉換為 com.apple.cocoa.foundation 中的陣列,反之亦然,沒有任何問題……好吧,幾乎沒有問題:從 Java 到 Obj-C,每個陣列都作為 NSCFArray 傳遞,該陣列**沒有**文件,但它可以像 NSArray 一樣工作。另一方面,從 Obj-C 到 Java,它們作為 com.apple.cocoa.NSMutableArray 傳遞,它繼承自普通的 NSArray——嘗試在它們透過橋接器傳遞後,在兩端列印它們的類描述,使用以下程式碼
[textField setStringValue:[[aJavaArray class] description]];
或者
textField.setStringValue(anObjCArray.getClass().toString());
這並不像看起來那麼糟糕。在實踐中,你不需要了解這一點。
真正的問題出現在你想要使用 EOF 時。大多數企業物件(在 com.webobjects.* 中的某個地方定義),當使用陣列時,它們都在等待 com.webobjects.foundation.NSArray,而不是上述任何一種。
這個問題有很多解決方案。第一個(對於那些瞭解 Java 和 Obj-C 的人)是在負責根據需要分配這些陣列的 bridgeTool.java 物件中。
public com.webobjects.foundation.NSArray arrayFromArray(com.apple.cocoa.foundation.NSArray cocoaArray)
{
com.webobjects.foundation.NSMutableArray woArray =
new com.webobjects.foundation.NSMutableArray();
int i;
for(i=0;i<cocoaArray.count();++i){
woArray.addObject(cocoaArray.objectAtIndex(i));
}
return woArray;
}
並使用類似以下內容從你的 Obj-C 物件中呼叫此方法:
NSArray* cocoaArray; // suppose this exists id bridgeTool = [[NSClassFromString(@"bridgeTool") new] autorelease]; id woArray = [bridgeTool arrayFromArray:cocoaArray];
獲得 woArray 後,你可以將其用作企業物件呼叫的引數。
我還沒有找到呼叫 Java 方法的方法,該方法的引數是一個 Object[] 陣列。如果有人閱讀本文並發現了方法,請告知我們……
在使用 EOF 時,在某些情況下,你可能希望構建一個限定符來執行獲取操作。限定符是 com.webobjects.eocontrol.EOQualifier 的一個例項。如果你的限定符不包含任何日期,則它就像這樣簡單:
NSString* partOfAName; // suppose this exists NSString* qualifierFormat = [NSString stringWithFormat:@"name caseInsensitiveLike '*%@*'", partOfAName]; id qualifier = [NSClassFromString(@"com.webobjects.eocontrol.EOQualifier") qualifierWithQualifierFormat:qualifierFormat :nil];
然後在獲取規範中使用它:
id fetchSpecification =
[[NSClassFromString(@"com.webobjects.eocontrol.EOFetchSpecification")
new] autorelease];
[fetchSpecification setEntity:@"myEntity"];
[fetchSpecification setQualifier:qualifier];
另一方面,如果你想在限定符中使用日期,由於 NSCalendarDate 沒有正確轉換為 NSTimestamp,你必須再次使用 bridgeTool.java 的技巧
public NSTimestamp nsTimestampFromString(String dateString){
NSTimestampFormatter formatter = new NSTimestampFormatter("%d %m %Y");
ParsePosition pp = new ParsePosition(0);
NSTimestamp myNSTimestamp = (NSTimestamp)formatter.parseObject(dateString, pp);
return myNSTimestamp;
}
並透過以下方式從 Obj-C 呼叫它:
NSCalendarDate aCalendarDate; // suppose this exists [aCalendarDate setCalendarFormat:@"%d %m %Y"]; id nsTimestamp = [bridgeTool nsTimestampFromString:[aCalendarDate description]];
(只需保持你的格式一致)。
也可以(至少在 Tiger 上執行的 WO 5.2.4 中)使用 com.webobjects.eointerface.cocoa.EOCocoaUtilities,如下所示:
id objPath = @"com.webobjects.eointerface.cocoa.EOCocoaUtilities"; id aDate = [NSCalendarDate date]; id nsTimestamp = [NSClassFromString(objPath) timestampForGregorianDate:aDate];
從這裡,你可以將 nsTimestamp 用作 com.webobjects.* 中定義的那些類(例如,在你的限定符中)的引數。
讓我們從本節開始描述如何將 OS X 的兩個功能最強大的框架整合在一起;即 EOF 和 NSDocument 框架(本節寫於 2005 年 7 月,當時正在使用 Tiger 上執行的 WO 5.2.4)——稍後,我們將把它們與 Core Data 整合,我認為在不久的將來,Core Data 結合 Bindings 將取代 EOF。
讓我們以“教程”的方式進行。
- 啟動 Xcode(我正在使用 2.0)並選擇“檔案”>“新建專案”。在下一個面板中選擇“Cocoa-Java 基於文件的應用程式”。這將生成一個專案,該專案已準備好使用 Java Bridge 並實現了 NSDocument 架構——檢視生成的 檔案和目標;特別是,雙擊主目標並檢查顯示的所有詳細資訊。
- 在專案視窗中,選擇“框架”資料夾。從“專案”選單中,選擇“新增到專案”(cmd+alt+a)。在瀏覽器中,轉到“系統”>“庫”>“框架”並選擇 JavaEOCocoa.framework;點選“確定”。
- 再次按下 cmd+alt+a,瀏覽到“系統”>“庫”>“框架”,然後到 JavaEOAccess.framework>“資源”>“Java”並選擇 javaeoaccess.jar;點選“確定”。
- 對以下檔案重複上一步:javaeocontrol.jar、javaeointerface.jar、javaeointerfacecocoa.jar、javafoundation.jar 和 javaxml.jar。
- 新增你的模型——仔細檢查其資料夾是否以藍色顯示(而不是黃色)。
基本上你已經完成了。但是,如果你——像我一樣——更喜歡在 Objective-C 中工作,請執行以下操作
- 透過雙擊 MyDocument.nib 檔案啟動 Interface Builder。
- 點選“檔案所有者”,然後點選“類”選項卡。你會注意到所選類是 MyDocument,它繼承自 NSDocument。選擇 NSDocument 並按“回車”建立新的子類;為其命名(例如,MyCDocument)。然後從“類”選單中選擇“建立檔案……”並接受。
- 返回到“例項”選項卡,選擇“檔案所有者”,並在“自定義類”檢查器(cmd+5)中選擇新建立的類。儲存並隱藏。
- 雙擊專案的“主目標”。選擇“文件型別”並根據新類進行相應的編輯(例如,將 MyDocument 更改為 MyCDocument)。
- 在三步下方建立的 .m 檔案中,新增以下內容:
- (NSString *)windowNibName
{
return @"MyDocument";
}
編譯並執行(cmd+r)。
最後,如果你願意,你可以消除嚮導建立的 .java 檔案中關於已棄用方法的警告
- 從專案視窗中刪除 MyDocument.java 檔案。
- 在主目標的“Cocoa Java 特定”窗格中選中“需要 Java”按鈕——在三步下方開啟的視窗中。
編譯並執行(cmd+r)。
至此,你已經建立了一個多文件 CEO……
-- StrauszRicardo