天天看點

視窗可不是什麼便宜的東西

盡管Windows作業系統以視窗為中心,但視窗本身可不是便宜的東西。更重要的是,1985 年那會嚴格的記憶體限制,迫使系統設計者做出各種不同的設計決策。

那我們以清單框控件舉例。

在現代設計中,你可以将清單框控件設計為一系列的子視窗清單,每個子視窗代表清單中的一個條目。 那麼,具有20000個項目的清單框将具有20000個子視窗。 然而,這種設計理念,在1985年是完全可笑的。

回想一下,當時的Windows是圍繞16位處理器建構的。視窗句柄是16位值,内部隻是接近 64K堆記憶體的指針。 一個視窗對象是88個位元組(我還真數過),這意味着在記憶體不足之前你最多可以建立大約700個左右的視窗。

更重要的是,菜單建立在同一個64K的記憶體堆中,是以實際限制要低得多。 即使視窗管理器在内部使用了大于64K的堆(Windows 95 就是這樣做的),20000 個視窗也超過了 1.5MB的記憶體。由于 8086 的最大位址空間為 1MB,即使你将每個位元組的記憶體都用于視窗對象,你仍然沒有足夠的記憶體。

此外,使每個清單框項成為視窗意味着每個清單框都将是一個可變高度的清單框,這帶來了管理具有可變高度項的容器的複雜性。

這違背了 API 設計的兩個一般原則:(1)簡單的事情應該簡單,(2)“付費遊戲”,如果你做簡單的事情,你不應該支付複雜事情的成本。

用實際視窗填充清單框也會使“虛拟清單框”的設計變得更加棘手。使用現有的設計,你可以說“有一百萬個項目”,而無需實際建立它們。 (這也是為什麼視窗空間被劃分為“客戶”和“非客戶”區域而不是讓非客戶區域由小子視窗組成的原因。)

為了保持與 16 位 Windows 程式的相容性(多虧了 WOW 層,這些程式仍然可以在 Windows XP運作),系統中的視窗句柄不能超過 65536 個,因為超過這個數就會影響 16 位程式的正常運作。(一旦建立了第 65537 個視窗,将有兩個具有相同 16 位句柄值的視窗,這要歸功于鴿巢原理。)(是的,即使在今天,16/32 位互操作性仍然很重要。)

如果系統可以建立的視窗上限為65536個視窗句柄,則包含100000個檔案的清單将遇到嚴重問題。 随着新功能不斷地被添加到視窗管理器中,視窗對象的大小随着時間的推移而增加。 今天它甚至比過去的88位元組還要大。 最好不要建立不必要的視窗。 如果你的應用程式設計需要你建立數千個視窗,則你應該考慮遷移到無視窗模型(windowless model),例如Internet Explorer、Word、清單框、樹視圖、清單視圖,甚至我們的滾動條示例程式。

通過無視窗模型,你擺脫了完整的視窗句柄的系統開銷,以及随之而來的所有包袱。 由于視窗句柄對所有程序都是可見的,是以集中管理視窗清單會産生很多開銷。 如果你的應用程式沒有視窗,那麼唯一可以通路你的内容的程式就是你自己的程式。 你不必擔心編組、跨程序同步、Unicode/ANSI 轉換、外部子類化、挂鈎……如果你想要的話,還可以使用1GB的記憶體來跟蹤你的無視窗資料,因為無視窗控件不影響任何其他程序。

其他程序可以通路視窗句柄這一事實,對可以在不影響整個系統的情況下建立多少個視窗句柄施加了實際的限制。

我相信WinFX使用了“螢幕上的一切都是元素”的模型。 據我了解,他們建構了一個無視窗架構,是以你就不用自己實作了。 (不過,我不确定這一點,我自己不是 WinFX 人。)

總結

需要警惕視窗數量的劇增,一方面這會增加管理難度,另一方面,程序的GDI對象太多,也會顯著降低運作時性能。

互動設計上,不妨簡單點,再簡單點,花裡胡哨不是我們所追求的,好的程式,應該是用完即走的。

最後