枚举器是一种苹果官方推荐的更加面向对象的一种遍历方式,相比于for循环,它具有高度解耦、面向对象、使用方便等优势
1.测试场景
一个未知内部数据的数组,判断数组中是否有字符串@"2"
方式:通过遍历,判断数组元素是否等于@"2"
[@[@"1",@"2",@"3"] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isEqualToString:@"2"]) {
NSLog(@"smart string is at index %lu",(unsigned long)idx);
*stop = YES;
}
}];
打印信息:smart string is at index 1
2.实现原理
你是否思考过它的内部实现方式?
那么让我们来重写一个它的内部实现,通过NSArray的分类给外界提供一个测试方法,来模拟它的内部实现(不开源,只能猜测 0^0)
- (void)enumTestBlock:(void (^)(id, NSUInteger, BOOL *))enumBlock {
if (!enumBlock && self.count == 0) return;
BOOL stop = NO;
for (int index = 0; index < self.count; index++ ) {
if (!stop) {
enumBlock(self[index], index, &stop);
} else {
break;
}
}
}
外部调用:
[@[@"1",@"2",@"3"] enumTestBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isEqualToString:@"2"]) {
NSLog(@"smart test string is at index %lu",(unsigned long)idx);
*stop = YES;
}
}];
打印信息:smart test string is at index 1
这里唯一需要注意就是Block内传入的一个bool类型的指针参数BOOL *stop
,是为了外部能在同一内存地址上修改内部实现中的变量stop的值,来实现跳出循环的功能
3.自定义枚举器实战场景
在看MJExtension源码时发现了这种有意思的自定义枚举器,用于遍历所有的类,直到遇到Foundation类时跳出循环
typedef void (^MJClassesEnumeration)(Class c, BOOL *stop);
+ (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration;
+ (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration {
// 1.没有block就直接返回
if (enumeration == nil) return;
// 2.停止遍历的标记
BOOL stop = NO;
// 3.当前正在遍历的类
Class c = self;
// 4.开始遍历每一个类
while (c && !stop) {
// 4.1.执行操作
enumeration(c, &stop);
// 4.2.获得父类
c = class_getSuperclass(c);
if ([MJFoundation isClassFromFoundation:c]) break;
}
}
相信这样的处理方式也是苹果官方更推荐的一种方式