Objective-C——在Cocoa Touch框架中使用迭代器模式
蘋果公司用自己的命名規則“列舉器/列舉”改寫了迭代器模式,用於相關基礎類的各種方法。從現在開始,我將使用“列舉”一詞,它就是蘋果版本的“迭代”。在這個模式中它們是一個意思。基礎框架中的NSEnumerator類實現了迭代器模式。抽象NSEnumerator類的私有具體子類返回列舉器物件,能夠順序遍歷各種集合——陣列、集(set)、字典(值與鍵),把集合中的物件返回給客戶端。
NSDirectoryEnumerator是個關係較遠的類。這個類的例項遞迴列舉檔案系統中一個目錄的內容。
NSArray、NSSet和NSDictionary這樣的集合類,定義了返回與集合的型別相應的NSEnumerator子類例項的方法。所有的列舉器都以同樣的方式工作。可以在一個迴圈中向列舉器傳送nextObject訊息,從列舉器取得物件,直到它返回nil表示遍歷結束。
NSEnumerator
從iOS 2.0開始,可以使用NSEnumerator來列舉NSArray、NSDictionary和NSSet物件中的元素。NSEnumerator本身是個抽象類。它依靠幾個工廠方法(見工廠方法模式,第4章),如objectEnumerator或keyEnumerator,來建立並返回相應的具體列舉器物件。客戶端用返回的列舉器物件遍歷集合中的元素,如下面的程式碼段所示。
NSArray *anArray = ... ;
NSEnumerator *itemEnumerator = [anArray objectEnumerator]
NSString *item;
while (item = [itemEnumerator nextObject])
{
// 對item作些處理
}
假設anArray儲存著一些NSString物件。在while迴圈中用NSString的方法對每個item進行處理。
當陣列的內容被取完之後,訊息呼叫[itemEnumerator nextObject]會返回nil,然後列舉過程就結束了。
從iOS 4開始,有了另一種列舉Cocoa Touch框架中集合物件的方法,它叫做基於塊的列舉。
基於塊的列舉
在iOS 4中為Cocoa Touch框架中的集合物件引入了基於塊的列舉(Block-Based Enumeration)。塊是Objective-C的一項語言功能(本書寫作時,蘋果公司還在爭取把塊作為對C語言的擴充套件而標準化)。塊是一種型別化的函式,就是說塊是函式也是型別。定義好的塊是一個可在方法呼叫之間傳遞的變數,就跟物件中的其他變數一樣。同時,塊變數在方法中可作為函式使用。當把塊作為引數傳遞給方法時,塊可以像C程式中的函式指標那樣被用作回撥函式。因此塊正適合於實現內部迭代器(列舉器)。客戶端不再需要手動生成迭代器,只需要提供一個符合目標集合物件所要求的簽名的塊。然後塊將在每個遍歷步驟中被呼叫。在每次塊被目標集合物件呼叫時,定義塊的演算法可以對返回的元素進行處理。
塊是Objective-C語言中很酷的一項功能。它讓我們可以把回撥演算法的定義內嵌在訊息呼叫之中。如果不使用塊,在Cocoa Touch框架中實現“回撥”的傳統方式是使用委託(見介面卡模式,第8章)。需要為要響應客戶端回撥的所有物件(介面卡)單獨定義一個協議(目標)。要是應用程式的這個部分複雜到需要另外的介面卡機制的程度,那也未嘗不可。有時塊可以提供一種比列舉器更漂亮的解決方案。
在iOS 4中,蘋果公司在NSArray、NSDictionary和NSSet物件中引入了新方法,用於基於塊的列舉。其中一個方法叫enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block。我們可以把自己的演算法定義在內嵌到訊息呼叫之中的塊裡,或者在別的什麼地方預先定義一個塊,然後作為引數傳給訊息呼叫。下面的程式碼段通過一個NSArray物件演示了它是如何在程式碼中實現的。
NSArray *anArray=[NSArray arrayWithObjects:@"This", @"is", @"a", @"test", nil];
NSString *string=@"test";
[anArray enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop)
{
if([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame)
{
// 對返回的obj做點別的事情
*stop=YES;
}
要是anArray物件中有個單詞是@"test",那麼就把指標*stop設定為YES,以通知anArray物件提前停止列舉。塊除了id obj和BOOL *stop引數,還有一個NSUInteger index引數。index引數讓塊中的演算法知道當前元素的位置,這對這樣的併發列舉非常有用。要是沒有這個引數,訪問索引的唯一方式就是使用indexOfObject:方法,這樣影響效率。
NSSet物件中基於塊的列舉與NSArray物件中的非常類似,只是在塊簽名中沒有index引數。NSSet物件是一種模擬“集合”(set)的資料結構,集合中的元素沒有表示元素在結構中位置的索引。
使用NSArray、NSDictionary和NSSet的內部迭代器的一個重要好處是,處理其內容的演算法可在其他地方由其他開發人員來定義。與傳統的for迴圈中定義的演算法不同,定義清晰的塊可被複用。當塊逐漸變大時,可把它們放到單獨的實現檔案中,不跟其他程式碼擠在一起。雖然塊是一種為複雜的事物新增內聯演算法的方便途徑,無需定義單獨的委託協議,但是當塊過大而難以維護時,應該考慮使用策略模式(第19章)。
快速列舉
Objective-C 2.0提供了一種列舉,稱為快速列舉。它是蘋果公司推薦的列舉方法。它允許把對集合物件的列舉直接用作for迴圈的一部分,無需使用其他列舉器物件,而且比傳統的基於索引的for迴圈效率更高。快速列舉的語法如下。
NSArray * anArray = ... ;
for (NSString * item in anArray)
{
// 對item作些處理
}
現在列舉迴圈使用指標運算(pointer arithmetic),讓它比使用NSEnumerator的標準方法效率更高。
要利用快速列舉,集合類需要實現NSFastEnumeration協議,以向執行庫提供關於集合的必要資訊。基礎框架中的所有集合類與NSEnumerator類都支援快速列舉。因此不必使用while迴圈從NSEnumerator列舉每個元素,直到nextObject返回nil,我們可以使用其快速列舉的版本,如下面的程式碼段所示。
NSArray * anArray = ... ;
NSEnumerator * itemEnumerator = [anArray objectEnumerator];
for (NSString * item in itemEnumerator)
{
// 對item作些處理
}
雖然既可以使用集合物件的快速列舉,也可以使用列舉器的快速列舉,但如果只需要預設遍歷(通常只按升序),直接對集合物件進行快速列舉更為合理。NSEnumerator使用其nextObject方法實現NSFastEnumeration協議。從效能上說,它比直接在while迴圈中手動呼叫這個方法好不了多少。儘管跟傳統的使用nextObject的while迴圈相比,快速列舉中的for迴圈顯得更為整潔。
實現NSFastEnumeration不在本書的範圍,所以不在此討論它。
內部列舉
NSArray有個例項方法叫(void)makeObjectsPerformSelector:(SEL)aSelector,它允許客戶端向陣列中每個元素髮送一個訊息,讓每個元素執行指定的aSelector(假定元素支援它)。可以用前面提到的任何一種列舉方法讓每個元素執行相同的選擇器,達到相同的目的。這個方法在內部列舉集合並向每個元素髮送performSelector:訊息。這種方式的缺點是如果集合中任何元素不響應選擇器,就會丟擲異常。因此它主要適用於不需太多執行時檢查的簡單操作。
相關文章
- Cocoa Touch 框架框架
- cocoa與cocoa Touch區別之分
- 設計模式 - 迭代器模式詳解及其在ArrayList中的應用設計模式
- 迭代器模式模式
- WWDC2017 筆記 - Cocoa Touch 中的新特性筆記
- WWDC 201 What's new in cocoa touch
- 迭代器模式(Iterator)模式
- 從迭代器模式到迭代協議模式協議
- 設計模式學習筆記(十六)迭代器模式及其在Java 容器中的應用設計模式筆記Java
- 行為型模式:迭代器模式模式
- 設計模式(十七)迭代器模式設計模式
- js設計模式--迭代器模式JS設計模式
- JS設計模式(迭代器模式)JS設計模式
- 設計模式之迭代器模式設計模式
- RxJS 中的觀察者和迭代器模式JS模式
- 《從零開始學Swift》學習筆記(Day 65)——Cocoa Touch設計模式及應用之選擇器Swift筆記設計模式
- golang設計模式之迭代器模式Golang設計模式
- Javascript設計模式之迭代器模式JavaScript設計模式
- 簡說設計模式——迭代器模式設計模式
- JavaScript 設計模式(六) 迭代器模式JavaScript設計模式
- 極簡設計模式-迭代器模式設計模式
- Java設計模式8:迭代器模式Java設計模式
- 設計模式(十六)迭代器設計模式
- 行為型:迭代器模式模式
- 【Objective-c】 ReactiveCocoa 框架使用ObjectReact框架
- 《從零開始學Swift》學習筆記(Day 63)——Cocoa Touch設計模式及應用之單例模式Swift筆記設計模式單例
- WWDC 2018:Cocoa Touch新特性與改進
- Cocoa Touch事件處理流程--響應者鏈(轉載)事件
- Reactive Cocoa 3.0 在 MVVM 中的應用ReactMVVM
- 23天設計模式之迭代器模式設計模式
- javascript設計模式 之 4 迭代器模式JavaScript設計模式
- C#設計模式之迭代器模式C#設計模式
- 設計模式-行為篇(迭代器模式)設計模式
- 23種設計模式之迭代器模式設計模式
- 遍歷聚合物件中的元素——迭代器模式(四)物件模式
- 遍歷聚合物件中的元素——迭代器模式(三)物件模式
- Swift與Cocoa框架開發Swift框架
- 常用開源框架中設計模式使用分析- 裝飾器模式(Decorator Pattern)框架設計模式