天天看點

《MonoTouch開發實踐指南》一2.3 MonoTouch的工作原理

monotouch使用靜态編譯方式将代碼編譯為arm二進制代碼。使用monotouch建立的每一個應用程式都是獨立的,也就是說,應用程式所需要的東西都要打包,之是以這樣,是因為iphone不允許使用共享庫。monotouch通過綁定方式向c#公開iphone的原生庫,因而不需要在語言之間做轉換。通過靜态編譯(ahead-of-time,aot)生成arm二進制代碼,monotouch應用程式就可滿足釋出應用程式到app store的所有必需條件。

注意 在寫本書的時候,許多使用monotouch開發的應用程式已經釋出到app store。在網站monotouch.info裡有這些應用程式的完整清單,并有大量相關資源的連結。現在還可以在檢視應用程式的展示。

然而,由于蘋果政策和核心的限制,jit編譯的代碼是不允許的。這就限制了.net的某些領域(如codedom、reflection-emit(盡管映射可以使用)、虛拟泛型方法、非确定性泛型和動态語言運作庫(dynamic language runtime,dlr))的使用。在一般情況下,大多數非ui的c#代碼都可以稍做修改或不做修改就能移植到monotouch中。

對于剛才在monotouch中建立的應用程式,現在來看看它們在monodevelop中生成時都做了什麼事情(例如在虛拟器生成一個釋出版本)。

設定配置為release | iphonesimulator,然後生成解決方案。生成後,在finder中浏覽工程目錄。

提示 在monodevelop解決方案樹中右擊該工程,并選擇open containing folder就可在finder中打開工程目錄。

在finder的工程目錄依次打開以下目錄:bin→iphonesimulator→release。在目錄中,可以看到生成的應用程式包。如果生成的是debug(調試)版本,就可在debug目錄中找到它,它在iphone目錄中而不是iphonesimulator目錄中。在所有情況下,目标目錄結構就是編譯應用時生成應用程式包的形式。有一個擴充名為.app的特殊檔案夾,它的作用是使應用程式看起來像單一的檔案,便于部署。對于目前的應用程式,會看到檔案的名字為lmt2-2.app。如果在finder中右擊該檔案并選擇show package contents,将看到如圖2-9所示的編譯後的執行檔案和應用程式所依賴的檔案。

《MonoTouch開發實踐指南》一2.3 MonoTouch的工作原理

各種dll檔案和exe檔案是剩餘的中繼資料。此外,兩個圖檔檔案用前面的build action of content,以及info.plist檔案和mainwindow.nib标記。檔案info.plist是一個屬性清單檔案,它是xml檔案,包含了應用程式不同中繼資料構成的字典。檔案mainwindow.nib是mainwindow.xib檔案的二進制版本。

注意 在部署應用程式到app store的時候,需要将.app檔案壓縮為zip檔案和一些圖檔檔案一起上傳。

在應用程式包中,最重要的檔案是lmt2-2,這是由aot編譯的,包含項目中所有的依賴項,包含monotouch提供的用于應用程式内管理記憶體的工具,是可在unix上執行的檔案。monotouch的記憶體管理與iphone上的objective-c不同,它是mono垃圾收集器的一個分支版本,負責應用程式代碼中所有的記憶體管理工作。

注意 mac os x中的objective-c 2.0并沒有垃圾收集器。此外,在模拟器上,monotouch使用jit編譯而不是aot。

回頭看看objective-c版本的應用程式,會注意到dealloc方法會發送一個釋放資訊給imageview;而在monotouch版本中,則不需要。其原因就是因為monotouch有垃圾收集器,而在iphone的objective-c中沒有。它依賴于基于引用計數(retain count)的記憶體管理模型。如果熟悉的話,會發現這有點兒類似于com,至少表面上是這樣。但是,uiimageview并不是一個.net的類,怎樣才能收集它呢?下面将講述這問題。

記憶體管理

要了解monotouch如何使用原生類管理記憶體,首先要了解objective-c管理記憶體的知識。在objective-c中,記憶體管理是通過引用計數來實作的。每當建立對象時,就會向objective-c發送一個alloc資訊,然後傳回對象(實際上是對象的指針,這裡把引用作為對象)和它的引用計數。每發送一個引用資訊給對象,對象的引用計數就會加1;而每當一個釋放資訊發送給對象,引用計數就會減1,當對象的引用計數為0時,記憶體就會釋放。

注意 objective-c的alloc和init版本的組合類似于c#中的構造函數。

此外,在許多情況下,工廠方法(factory method)模式的類隻傳回一個對象。在這種情況下,如果配置設定記憶體後,工廠方法在内部釋放對象,那麼在調用者有機會使用它之前,該對象将銷毀,這時,如果工廠方法對這視而不見,并傳回了該配置設定對象(包含引用計數),那麼該對象将永遠不會銷毀。通過由nsautoreleasepool類實作的自動釋放池(autorelease pool),可以解決這個問題。基本上,nsautoreleasepool會延遲對象的銷毀,這樣調用者就能在它銷毀前使用它。在iphone的引用計數世界中,對象會添加到自動釋放池直到釋放池自身發出一個遺漏(drain)資訊(或釋放資訊)時,才會發送釋放資訊。隻要釋放池中的對象不明确地發送一個保留資訊,對象就會銷毀。

當任何帶有引用計數為1的對象釋放時,就會調用dealloc方法,這時就可以調用對象的釋放方法來清理對象(即釋放對象的指針)。

基于以上資訊,現在探讨monotouch如何與原生代碼互動。對于objective-c對象,monotouch在主線程建立一個自動釋放池和線程池線程。是以,每當線程使用objective-c類時,monotouch開發人員就要建立一次自動釋放池,例如,使用以下代碼建立線程:

在“dosomething”方法内要使用objective-c對象(也就是說,從nsobject派生的任何類),都需要将代碼包含在自動釋放池内,如以下代碼:

提示 如果在運作時出現“autoreleased with no pool in place—just leaking,”這樣的警告,如之前所示那樣将代碼包含到nsautoreleasepool中即可。

除此之外,我們發現monotouch在記憶體管理方面做得相當出色。當然,.net對象會通過monotouch gc自身進行管理。需要記憶體管理的其他地方,可能還需要采取更精細的記憶體控制,或者至少清楚nsobject基類的idisposable實作。在monotouch端,會發送釋放資訊到nsobject對象子類的執行個體(附帶一些内部資料結構的清理),如果要銷毀的是占用大量記憶體的對象,這非常有用,而且宜早不宜遲。對于nsautoreleasepool,當釋放資訊發送到釋放池時,會将釋放資訊發送到它内部所有的自動釋放對象。

繼續閱讀