從 C++ 到 Objective-C(19):STL 和 Cocoa
C++ 標準庫是其強大的一個原因。即使它還有一些不足,但是已經能夠算作是比較完備的了。這並不是語言的一部分,而是屬於一種擴充套件,其他語言也有類似的部分。在 Objective-C 中,你不得不在 Cocoa 裡面尋找容器、遍歷器或者其他一些真正可以使用的演算法。
容器
Cocoa 的容器比 C++ 更加面向物件,它不使用模板實現,只能存放物件。現在可用的容器有:
-
NSArray
和NSMutableArray
:有序集合; -
NSSet
和NSMutableSet
:無序集合; -
NSDictionary
和NSMutableDictionary
:鍵值對形式的關聯集合; -
NSHashTable
:使用弱引用的散列表(Objective-C 2.0 新增)。
你可能會發現這其中並沒有 NSList 或者 NSQueue。事實上,這些容器都可以由 NSArray
實現。
不同於 C++ 的 vector<T>
,Objective-C 的 NSArray
真正隱藏了它的內部實現,僅能夠使用訪問器獲取其內容。因此, NSArray
沒有義務為記憶體單元優化其內容。 NSArray
的實現有一些妥協,以便 NSArray
能夠像陣列或者列表一樣使用。既然 Objective-C 的容器只能存放指標,單元維護就會比較有效率了。
NSHashTable
等價於 NSSet
,但它使用的是弱引用(我們曾在前面的章節中講到過)。這對於垃圾收集器很有幫助。
遍歷器
經典的列舉
純面向物件的實現讓 Objective-C 比 C++ 更容易實現遍歷器。 NSEnumerator
就是為了這個設計的:
NSArray* array = [NSArray arrayWithObjects:object1, object2, object3, nil]; NSEnumerator* enumerator = [array objectEnumerator]; NSString* aString = @"foo"; id anObject = [enumerator nextObject]; while (anObject != nil) { [anObject doSomethingWithString:aString]; anObject = [enumerator nextObject]; }
容器的 objectEnumerator
方法返回一個遍歷器。遍歷器可以使用 nextObject
移動自己。這種行為更像 Java 而不是 C++。當遍歷器到達容器末尾時, nextObject
返回 nil
。下面是最普通的使用遍歷器的語法,使用的 C 語言風格的簡寫:
NSArray* array = [NSArray arrayWithObjects:object1, object2, object3, nil]; NSEnumerator* enumerator = [array objectEnumerator]; NSString* aString = @"foo"; id anObject = nil; while ((anObject = [enumerator nextObject])) { [anObject doSomethingWithString:aString]; } // 雙括號能夠防止 gcc 發出警告
快速列舉
Objective-C 2.0 提供了一個使用遍歷器的新語法,隱式使用 NSEnumerator
(其實和一般的 NSEnumerator
沒有什麼區別)。它的具體形式是:
NSArray* someContainer = ...; for(id object in someContainer) { // 每一個物件都是用 id 型別 ... } for(NSString* object in someContainer) { // 每一個物件都是 NSString ...// 開發人員需要處理不是 NSString* 的情況 }
函式物件
使用選擇器
Objective-C 的選擇器很強大,因而大大減少了函式物件的使用。事實上,弱型別允許使用者無需關心實際型別就可以傳送訊息。例如,下面的程式碼同前面使用遍歷器的是等價的:
NSArray* array = [NSArray arrayWithObjects:object1, object2, object3, nil]; NSString* aString = @"foo"; [array makeObjectsPerformSelector:@selector(doSomethingWithString:) withObject:aString];
在這段程式碼中,每個物件不一定非得是 NSString
型別,並且物件也不需要必須實現了 doSomethingWithString:
方法(這會引發一個異常:selector not recognized)。
IMP 快取
我們在這裡不會詳細解釋這個問題,但是的確可以獲得 C 函式的記憶體地址。通過僅查詢一次函式地址,可以優化同一個選擇器的多次呼叫。這被稱為 IMP 快取,因為 Objective-C 用於方法實現的資料型別就是 IMP。
呼叫 class_getMethodImplementation()
就可以獲得這麼一個指標。但是請注意,這是指向實現方法的真實的指標,因此不能有虛呼叫。它的使用一般在需要很好的時間優化的場合,並且必須非常小心。
演算法
STL 中那一大堆通用演算法在 Objective-C 中都沒有對等的實現。相反,你應該仔細查詢下各個容器中有沒有你需要的演算法。
- Dive Into HTML5:可擴充套件性(續四)
- 從 C 到 Objective-C(4):類和物件(續)
- Qt 學習之路 2(94):使用 C 擴充套件 QML(續)
- Dive Into HTML5:檢測 HTML5 特性
- Qt 學習之路 2(87):模型-檢視高階技術
- 從 C 到 Objective-C(19):STL 和 Cocoa
- Qt 學習之路 2(19):事件的接受與忽略
- Qt Creator 外掛開發(2):第一個外掛
- Dive Into HTML5:Web 視訊(續)
- Qt 學習之路 2(77):QML 語法
- 編寫 native 風格的 Qt 程式(1)
- Dive Into HTML5:地理位置(續)
- 關係資料庫是如何工作的(2)
- Dive Into HTML5:歷史(續三)
- 從 C 到 Objective-C(21):隱式程式碼(續)
- Qt Creator 外掛開發(3):新增選單項
- 從 C 到 Objective-C(16):記憶體管理(續三)
- 理解 Flex 4 元件生命週期(3)
- 高階 TypeScript:對映型別
- Qt 4 外掛開發(4)