OC : NSFastEnumeration (快速枚举)
来源:互联网 发布:物流打印软件 编辑:程序博客网 时间:2024/06/06 04:36
NSFastEnumeration
快速枚举有两个优点。
一是,实现快速枚举后,你可以直接使用for/in语法遍历你的对象。
二是,如果将快速枚举实现得很好,会大大提高遍历的速度。
实现快速枚举,很简单。只需要实现NSFastEnumeration
协议就可以了,而且这个协议只有一个方法:
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained _Nullable [_Nonnull])buffer count:(NSUInteger)len;
NSFastEnumerationState
是什么啊?
typedef struct { unsigned long state; id __unsafe_unretained _Nullable * _Nullable itemsPtr; unsigned long * _Nullable mutationsPtr; unsigned long extra[5];} NSFastEnumerationState;
概括地说,这个方法就是用于返回一系列的 C 数组,以供调用者进行遍历。
为什么是一系列的 C 数组呢?因为,在一个for/in
循环中,这个方法其实会被调用多次,每一次调用都会返回一个 C 数组。至于为什么是 C 数组,那当然是为了提高效率了。
每次调用返回一组对象,这样使对象可以成批的被返回给调用者。
为了获得最好的运行效率,这里使用了一个C数组,这样就需要两个参数——数组首地址指针和数组长度。
数组长度被作为返回值返回。
这也是方法名字中count
所代表的意思。
数组的首地址指针由NSFastEnumerationState
结构体中的itemsPtr
字段指定。这两个值共同确定了这个方法返回给调用者的数组。
NSFastEnumeration
被设计成允许返回一个指向内存的指针。
然而,这并不适于所有数据结构,因此它也被设计成允许将内部对象拷贝到调用者提供的一个数组容器中。
在这里,buffer
即是指调用者提供的数组容器,len
是数组容器的大小。
NSFastEnumeration
还被设计成在遍历集合的时候,可以检测到集合的变动。如果检测到集合被修改了,就会抛出一个异常。mutationsPtr
被用于指向会随着集合变动而变化的一个值。
好了,大部分字段和参数的意思都讲解完了。唯一没讲的就是state和extra这两个字段。这两个字段是预留给被调用者的,你可以自由的使用它们来存储任何你觉得有用的数据。
实现
NSArray *collection = @[@"1",@"2",@"3"]; for(id obj in collection) { // body NSLog(@"%@",obj); }
clang -rewrite-objc xxx.m
查看.cpp
文件
//创建数组 NSArray *collection = ((NSArray *(*)(Class, SEL, const ObjectType *, NSUInteger))(void *)objc_msgSend)(objc_getClass("NSArray"), sel_registerName("arrayWithObjects:count:"), (const id *)__NSContainer_literal(3U, (NSString *)&__NSConstantStringImpl__var_folders_yb_zbqcspv53295085k0yntnvm00000gn_T_Bridge_30637e_mi_0, (NSString *)&__NSConstantStringImpl__var_folders_yb_zbqcspv53295085k0yntnvm00000gn_T_Bridge_30637e_mi_1, (NSString *)&__NSConstantStringImpl__var_folders_yb_zbqcspv53295085k0yntnvm00000gn_T_Bridge_30637e_mi_2).arr, 3U); { id obj; //初始化结构体 NSFastEnumerationState struct __objcFastEnumerationState enumState = { 0 }; //初始化数组 stackbuf id __rw_items[16]; id l_collection = (id) collection; // 第一次调用 - countByEnumeratingWithState:objects:count: 方法,形参和实参的对应关系如下: // state -> &enumState // stackbuf -> __rw_items // len -> 16 _WIN_NSUInteger limit = ((_WIN_NSUInteger (*) (id, SEL, struct __objcFastEnumerationState *, id *, _WIN_NSUInteger))(void *)objc_msgSend) ((id)l_collection, sel_registerName("countByEnumeratingWithState:objects:count:"), &enumState, (id *)__rw_items, (_WIN_NSUInteger)16); if (limit) { // 获取 mutationsPtr 的初始值 unsigned long startMutations = *enumState.mutationsPtr; do { unsigned long counter = 0; do { if (startMutations != *enumState.mutationsPtr) objc_enumerationMutation(l_collection); obj = (id)enumState.itemsPtr[counter++]; { NSLog((NSString *)&__NSConstantStringImpl__var_folders_yb_zbqcspv53295085k0yntnvm00000gn_T_Bridge_30637e_mi_3,obj); }; __continue_label_1: ; } while (counter < limit); } while ((limit = ((_WIN_NSUInteger (*) (id, SEL, struct __objcFastEnumerationState *, id *, _WIN_NSUInteger))(void *)objc_msgSend) ((id)l_collection, sel_registerName("countByEnumeratingWithState:objects:count:"), &enumState, (id *)__rw_items, (_WIN_NSUInteger)16))); obj = ((id)0); __break_label_1: ; } else obj = ((id)0); }
如上代码所示,快速枚举其实就是用两层 do/while
循环来实现的,外层循环负责调用 - countByEnumeratingWithState:objects:count:
方法,获取 C 数组,而内层循环则负责遍历获取到的 C 数组。
同时,我想你应该也注意到了它是如何利用mutationsPtr
来检测集合在遍历过程中的突变的,以及使用 objc_enumerationMutation
函数来抛出异常。
正如我们前面提到的,在快速枚举的实现中,确实没有用到结构体 NSFastEnumerationState
中的 state
和extra
字段,它们只是提供给 - countByEnumeratingWithState:objects:count:
方法的实现者自由使用的字段。
推荐阅读
http://nshipster.com/enumerators/
http://www.cocoachina.com/ios/20160718/17060.html
http://www.jianshu.com/p/aa50d75f3795
- OC : NSFastEnumeration (快速枚举)
- OC的快速枚举
- OC 快速枚举总结
- OC中的快速枚举器
- [iOS/OC]快速枚举enumerateObjectsUsingBlock
- OC中的快速枚举与普通遍历
- OC 中快速枚举器简单讲解.
- OC - 集合类型的快速枚举
- OC基本功(三)使用枚举
- 快速枚举因子(约数)
- OC枚举和Swift 枚举
- 快速枚举
- 快速枚举
- 快速枚举
- 快速枚举
- 快速枚举
- 快速枚举
- 快速枚举
- proxool连接池配置
- 09:明明的随机数
- SpringMVC项目的搭建过程
- 浅拷贝和深拷贝
- Python 基础语法
- OC : NSFastEnumeration (快速枚举)
- 一个C++异步队列的测试程序
- Python 设计模式
- 如何搭建直播平台?低延时连麦+人工智能让互动升级
- 【DIY】打造一个六足蜘蛛机器人
- PHP通过PHP QR Code生成二维码
- Spring MVC中快速集成swagger2
- Oracle介绍(一)
- ionic3三种跳转方法对应的页面间数据传递及Events