FastEnumerationSample
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
FastEnumerationSample.xcodeproj
FastEnumerationSample
.gitignore
README.md

README.md

FastEnumerationSample

我的博文《Objective-C Fast Enumeration 的实现原理》的完整配套代码:

@interface Array : NSObject <NSFastEnumeration>

- (instancetype)initWithCapacity:(NSUInteger)numItems;

@end

@implementation Array {
    std::vector<NSNumber *> _list;
}

- (instancetype)initWithCapacity:(NSUInteger)numItems {
    self = [super init];
    if (self) {
        for (NSUInteger i = 0; i < numItems; i++) {
            _list.push_back(@(random()));
        }
    }
    return self;
}

#define USE_STACKBUF 1

- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])stackbuf count:(NSUInteger)len {
    // 这个方法的返回值,即我们需要返回的 C 数组的长度
    NSUInteger count = 0;
    
    // 我们前面已经提到了,这个方法是会被多次调用的
    // 因此,我们需要使用 state->state 来保存当前遍历到了 _list 的什么位置
    unsigned long countOfItemsAlreadyEnumerated = state->state;
    
    // 当 countOfItemsAlreadyEnumerated 为 0 时,表示第一次调用这个方法
    // 我们可以在这里做一些初始化的设置
    if (countOfItemsAlreadyEnumerated == 0) {
        // 我们前面已经提到了,state->mutationsPtr 是用来追踪集合在遍历过程中的突变的
        // 它不能为 NULL ,并且也不应该指向 self
        //
        // 这里,因为我们的 Array 类是不可变的,所以我们不需要追踪它的突变
        // 因此,我们的做法是将它指向 state->extra 的其中一个值
        // 因为我们知道 NSFastEnumeration 协议本身并没有用到 state->extra
        //
        // 但是,如果你的集合是可变的,那么你可以考虑将 state->mutationsPtr 指向一个内部变量
        // 而这个内部变量的值会在你的集合突变时发生变化
        state->mutationsPtr = &state->extra[0];
    }
    
#if USE_STACKBUF
    
    // 判断我们是否已经遍历完 _list
    if (countOfItemsAlreadyEnumerated < _list.size()) {
        // 我们知道 state->itemsPtr 就是这个方法返回的 C 数组指针,它不能为 NULL
        // 在这里,我们将 state->itemsPtr 指向调用者提供的 C 数组 stackbuf
        state->itemsPtr = stackbuf;
        
        // 将 _list 中的元素填充到 stackbuf 中,直到以下两个条件中的任意一个满足时为止
        // 1. 已经遍历完 _list 中的所有元素
        // 2. 已经填充满 stackbuf
        while (countOfItemsAlreadyEnumerated < _list.size() && count < len) {
            // 取出 _list 中的元素填充到 stackbuf 中
            stackbuf[count] = _list[countOfItemsAlreadyEnumerated];
            
            // 更新我们的遍历位置
            countOfItemsAlreadyEnumerated++;
            
            // 更新我们返回的 C 数组的长度,使之与 state->itemsPtr 中的元素个数相匹配
            count++;
        }
    }
    
#else
    
    // 判断我们是否已经遍历完 _list
    if (countOfItemsAlreadyEnumerated < _list.size()) {
        // 直接将 state->itemsPtr 指向内部的 C 数组指针,因为它的内存地址是连续的
        __unsafe_unretained const id * const_array = _list.data();
        state->itemsPtr = (__typeof__(state->itemsPtr))const_array;
        
        // 因为我们一次性返回了 _list 中的所有元素
        // 所以,countOfItemsAlreadyEnumerated 和 count 的值均为 _list 中的元素个数
        countOfItemsAlreadyEnumerated = _list.size();
        count = _list.size();
    }
    
#endif
    
    // 将本次调用得到的 countOfItemsAlreadyEnumerated 保存到 state->state 中
    // 因为 NSFastEnumeration 协议本身并没有用到 state->state
    // 所以,我们可以将这个值保留到下一次调用
    state->state = countOfItemsAlreadyEnumerated;
    
    // 返回 C 数组的长度
    return count;
}

@end