Objective-C中的快速枚举和NSEnumerator

11

我一再看到这个问题,为什么在循环中使用快速枚举比使用NSEnumeratornextObject:更快呢。

3个回答

30

NSEnumerator是遍历集合的一种老方法。它涉及创建一个对象来表示枚举,然后对每个迭代调用其上的方法。虽然这在许多年内可以完美地服务,但效率不高,因为它涉及到至少一个消息发送来进行循环迭代。而NSFastEnumeration则是更现代的方法,利用本地语言支持提供了更加高效的枚举方式。其内部工作方式是创建一个结构体来表示当前的枚举状态,并在集合上重复调用-countByEnumeratingWithState:objects:count:方法。此方法会将一个C数组的对象放入objects输出参数中,并在count输出参数中返回计数器。这使得调用者可以在C数组上进行迭代。实质上,这意味着每个对象块只需要一个消息调用,这可以像获取全部对象的单个消息调用一样高效(具体取决于集合)。

如果你有一些类似以下代码:

for (id obj in myArray) {
    [obj doSomething];
}

这将被编译器翻译为大致相当于以下内容:

NSFastEnumerationState __enumState = {0};
id __objects[MAX_STACKBUFF_SIZE];
NSUInteger __count;
while ((__count = [myArray countByEnumeratingWithState:&__enumState objects:__objects count:MAX_STACKBUFF_SIZE]) > 0) {
    for (NSUInteger i = 0; i < __count; i++) {
        id obj = __objects[i];
        [obj doSomething];
    }
}

实际使用的变量被隐藏,对象缓冲区的最大大小也因实现而异,但基本思想是一样的。它将对 obj-c 集合的迭代转换为对 C 数组的迭代。


7
...而且enumerateObjectsUsingBlock:在枚举NSArray时速度非常快,而在枚举NSDictionary的值时速度更快。 - bbum

2

虽然与苹果的实现不同,但这对理解很有帮助。

- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*)state  
                   objects: (id*)stackbuf
                     count: (NSUInteger)len
{
  IMP nextObject = [self methodForSelector: @selector(nextObject)];
  int i;

  state->itemsPtr = stackbuf;
  state->mutationsPtr = (unsigned long*)self;
  for (i = 0; i < len; i++)
    {
      id next = nextObject(self, @selector(nextObject));

      if (nil == next)
    {
      return i;
    }
      *(stackbuf+i) = next;
    }
  return len;
}

0
NSArray *array = something;

数组 = { {1,2}, {2,3}, {3,4} } 这意味着数组是一个数组的数组。那么如何访问所有的数组及其值呢? 我们可以使用如下的for循环:
for (int i = 0; i < array.count; i++)
{
    NSArray x = [array objectAtIndex:i];
}

或者一个快速枚举的工作方式如下

for(NSArray array2 in array)
{
   // do what ever you want with this new array2.
}

这是一个示例例子。
PS. 我忘记了控制台中数组的样子。


1
这只是语法糖,它并没有回答为什么一个比另一个更快。 - shreyasva

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接