Objective-c:集合類型遍歷方法詳解

前言

在編程中經常需要遍歷集合類型的元素,當前有多種方法實現此功能,初學者大多習慣於用標準的C語言循環,當然也可以使用 NSEnumerator 以及快速遍歷(for-in),Objective-c 引入‘塊’特性後,又多出來幾種新的遍歷方式,採用這幾種新的遍歷方式,可大幅度簡化代碼過程。

本文主要講解常用的幾種集合類型:NSArrayNSSet 以及NSDictionary 的遍歷方式。

for 循環遍歷

  • 遍歷數組(NSArray)
// 初始化數組
NSArray *numbers = @[@1, @2, @3, @4, @5];

// 遍歷數組元素
for (int i = 0; i < numbers.count; i++) {
    id obj = numbers[i];
    NSLog(@"%@", obj);
}
  • 遍歷集(NSSet)
// 初始化集
NSSet *sets = [NSSet setWithObjects:@1, @2, @3, @4, @5, nil];

// 獲取集元素
NSArray *objects = [sets allObjects];

// 遍歷集元素
for (int i = 0; i < objects.count; i++) {
    id obj = objects[i];
    NSLog(@"%@", obj);
}
  • 遍歷字典(NSDictionary)
// 初始化字典
NSDictionary *infomation = @{@"account" : @"admin",
                             @"password": @123456,
                             @"members" : @YES,
                             @"state"   : @"activation"};

// 獲取字典鍵
NSArray *keys = [dicts.allKeys mutableCopy];

// 遍歷字典鍵值對
for (int i = 0; i < keys.count; i++) {
    id key = keys[i];
    id obj = dicts[key];

    NSLog(@"%@", obj);
}

Tips

1、字典與set都是“無序”的,所以無法根據特定的整數下標來直接訪問其中的值,於是,在遍歷的時候需要獲取字典的所有鍵或是set裏的所有對象,此過程將會造成額外開銷。

2、for循環也可以實現方向遍歷,計數器的值從“元素個數減1”開始,每次迭代時遞減,直到0爲止。執行方向遍歷時,使用for循環會比其他方式簡單很多。

NSEnumerator 遍歷

  • NSEnumerator提供的nextObject方法,返回枚舉裏的下個對象。每次調用該方法時,其內部數據結構都會更新,使得下次調用方法時能返回下個對象。等到枚舉中的全部對象都已返回之後,再調用就將返回nil,這表示達到枚舉末端了。

  • 遍歷數組(NSArray)

// 初始化數組
NSArray *numbers = @[@1, @2, @3, @4, @5];

// 創建枚舉器
NSEnumerator *enumerator = [numbers objectEnumerator];

// 創建迭代元素
id obj = nil;

// 遍歷數組元素
while ((obj = [enumerator nextObject]) != nil) {
    NSLog(@"%@", obj);
}
  • 遍歷集(NSSet)
// 初始化集
NSSet *sets = [NSSet setWithObjects:@1, @2, @3, @4, @5, nil];

// 獲取集元素
NSArray *objects = [sets allObjects];

id obj = nil;

// 遍歷集元素
while ((obj = [enumerator nextObject]) != nil) {
    NSLog(@"%@", obj);
}

  • 遍歷字典(NSDictionary)
// 初始化字典
NSDictionary *infomation = @{@"account" : @"admin",
                             @"password": @123456,
                             @"members" : @YES,
                             @"state"   : @"activation"};

NSEnumerator *enumerator = [infomation keyEnumerator];

id key = nil;

// 遍歷字典鍵值對
while ((key = [enumerator nextObject]) != nil) {
    id obj = dicts[key];
    NSLog(@"%@ : %@", key, obj);
}
  • 遍歷字典的方式與數組和set略有不同,因爲字典裏既有鍵也有值,所以要根據給定的鍵把對應的值提取出來。使用NSEnumerator還有個好處,就是有多種枚舉器可供使用。比如,有反向遍歷數組的枚舉器,如果拿它來遍歷,就可以按反方向來迭代集合中的元素了。
NSArray *numbers = @[@1, @2, @3, @4, @5];

NSEnumerator *enumerator = [numbers reverseObjectEnumerator];

id obj = nil;

while ((obj = [enumerator nextObject]) != nil) {
    NSLog(@"%@", obj);
}

// 輸出:5 4 3 2 1

快速遍歷

  • 快速遍歷與使用NSEnumerator來遍歷差不多,然而語法更簡潔,它爲for循環開設了in關鍵字。這個關鍵字大幅簡化了遍歷集合所需的語法。

  • 遍歷數組(NSArray)

NSArray *numbers = @[@1, @2, @3, @4, @5];

for (id obj in numbers) {
    NSLog(@"%@", obj);
}
  • 遍歷集(NSSet)
NSSet *sets = [NSSet setWithObjects:@1, @2, @3, @4, @5, nil];

for (id obj in sets) {
    NSLog(@"%@", obj);
}
  • 遍歷字典(NSDictionary)
NSDictionary *infomation = @{@"account" : @"admin",
                             @"password": @123456,
                             @"members" : @YES,
                             @"state"   : @"activation"};

for (id key in dicts.allKeys) {
    id obj = dicts[key];
    NSLog(@"%@ : %@", key, obj);
}
  • 由於NSEnumerator對象也實現了NSFastEnumeration協議,所以能用來執行方向遍歷。若要反向遍歷數組,可採用下面這種寫法:
NSArray *numbers = @[@1, @2, @3, @4, @5];

for (id obj in [numbers reverseObjectEnumerator]) {
    NSLog(@"%@", obj);
}
// 輸出:5 4 3 2 1

基於塊的遍歷方式

  • 在遍歷數組及set時,每次迭代都要執行由block參數所傳入的塊,這個塊有三個參數,分別是當前迭代所針對的對象、所針對的下標、以及指向布爾值的指針。前兩個參數的含義不言而喻。而通過第三個參數所提供的機制,開發者可以終止遍歷操作。

  • 遍歷數組(NSArray)

NSArray *numbers = @[@1, @2, @3, @4, @5];

[numbers enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

    NSLog(@"%@", obj);

    if (idx == 3) {
        *stop = YES;
    }
}];
  • 遍歷集(NSSet)
NSSet *sets = [NSSet setWithObjects:@1, @2, @3, @4, @5, nil];

[sets enumerateObjectsUsingBlock:^(id  _Nonnull obj, BOOL * _Nonnull stop) {
    NSLog(@"%@", obj);

    if(shouldStop) {
       *stop = YES;
    }
}];
  • 遍歷字典(NSDictionary)
NSDictionary *infomation = @{@"account" : @"admin",
                             @"password": @123456,
                             @"members" : @YES,
                             @"state"   : @"activation"};

[infomation enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
    NSLog(@"%@:%@", key, obj);

    if(shouldStop) {
       *stop = YES;
    }
}];
  • 通過基於塊的遍歷方式可以直接從塊裏獲取更多信息。在遍歷數組時,可以知道當前所針對的下標。遍歷有序set時也一樣。而在遍歷字典時,無需額外編碼,即可同時獲取鍵與值,因而省去了根據給定鍵來獲取對應值這一步。用這種方式遍歷字典,可以同時得知鍵與值,這很可能比其他方式快很多,因爲在字典內部的數據結構中,鍵與值本來就是存儲在一起的。

  • 另外一個好處是,能夠修改塊的方法前面,以免進行類型轉換操作,從效果上講,相當於把本來需要執行的類型轉換操作交給塊方法簽名來做。比方說,要用“快速遍歷法”來遍歷字典。若已知字典中的對象必須爲字符串,則可以這樣編碼:

for (NSString *key in aDictionary) {

    NSString *obj = (NSString *)aDictionary[key];

}
  • 如果改用基於快的方式來遍歷,那麼就可以在塊方法簽名中直接轉換:
NSDictionary *aDictionary = /* ... */

[aDictionary enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString *  _Nonnull obj, BOOL * _Nonnull stop) {
    // Do something with 'key' and 'obj'
}];
  • 基於塊的遍歷方式同樣可以執行反向遍歷,通過NSEnumeratorReverse選項來實現,要注意:只有在遍歷數組或有序set等集合的時候纔有意義,此處以數組爲例。
NSArray *numbers = @[@1, @2, @3, @4, @5];

[numbers enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    NSLog(@"%@", obj);
}];

// 輸出:5 4 3 2 1

要點

  • 遍歷集合有四種方式。最基本的是for循環,其次是NSEnumerator遍歷法及快速遍歷法,最新、最先進的方式則是“塊枚舉法”。

  • 若提前知道遍歷的集合含有何種對象,則應修改塊簽名,指出對象的具體類型。

發佈了95 篇原創文章 · 獲贊 148 · 訪問量 39萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章