我们在用Objective-C编写程序时,很多时候会用到NSArray来作为线性列表来使用。我们在枚举这个数组所有元素的使用可以通过下列方法进行:

for(id obj in anArray)
{ }

这种方式在编程语言术语中也被称为for-each形式。在C++11以及Java 5中,上述的in使用冒号:来表示。

那么我们在Objective-C中是否可以自己定义一个类来实现for-each形式呢?当然可以!我们可以通过两种方式来实现这种简单的for-each语法形式。

1、通过继承NSEnumerator类,并且重写其- (NSArray*)allObjects方法以及- (id)nextObject方法来实现。

2、通过实现NSFastEnumeration协议,并实现其- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id*)stackbuf count:(NSUInteger)len方法。

由于NSEnumerator类比较简单,我们可以通过看下面的代码示例即可理解。但是由于Objective-C只有单继承,因此使用对NSFastEnumeration协议的实现会更加灵活。而且这里比较复杂的是countByEnumeratingWithState方法的实现。下面先对这个方法做个简单介绍。

首先,当你的类实现了NSFastEnumeration协议并实现了countByEnumeratingWithState之后,在用for-each时,这个countByEnumeratingWithState可能需要被执行多次,依赖于你所要枚举的元素个数。因此,这个方法的返回值指示了当前所要枚举的数组的元素个数,如果返回0,则说明枚举全部完成。

参数stackbuf是指向方法调用者(即消息发送者)所分配的栈空间。这个是由编译器自动分配的,一般不需要程序员自己干涉。

参数len表示栈空间分配的大小(sizeof(id) * len个字节),也就是说单次枚举最大能放多少元素。

参数state指向由编译器给我们分配好的一个NSFastEnumerationState结构体变量地址。这个参数需要我们实现中自己来设置。我们先看这个结构体的定义:

typedef struct {
unsigned long state; // 表示当前状态,初始为0
id __unsafe_unretained *itemsPtr; // 指向当前所要枚举的数组首地址
unsigned long *mutationsPtr; // 用于检测,所要枚举的对象是否发生了改变
unsigned long extra[]; // 这里可以由实现者随意存放必要的额外数据
} NSFastEnumerationState;

对于这个结构体变量,itemsPtr与mutationsPtr必须进行设置,并且不能为空,除非,此次枚举直接返回0。

下面我们通过代码示例来详细描述这两种方法的使用:

//
// main.m
// objCTest
//
// Created by Zenny Chen on 12-2-7.
// Copyright (c) 2014年 Neon Media Studio. All rights reserved.
// #import <Foundation/Foundation.h> @interface MyIterator : NSEnumerator
{
@private NSArray *mArray;
int mCurrIndex;
} @end @implementation MyIterator - (instancetype)init
{
self = [super init]; mArray = [@[@, @, @, @, @, @, @, @] retain]; return self;
} - (void)dealloc
{
if(mArray != nil)
{
[mArray release];
mArray = nil;
} [super dealloc];
} - (NSArray*)allObjects
{
return mArray;
} - (id)nextObject
{
if(mCurrIndex == [mArray count])
return nil; return [mArray objectAtIndex:mCurrIndex++];
} @end @interface MyFastIterator : NSObject<NSFastEnumeration>
{
@private NSArray *mArray;
NSNumber* mNumbers[];
} @end @implementation MyFastIterator - (instancetype)init
{
self = [super init]; // 先对mArray进行初始化
mArray = [@[@, @, @, @, @-, @-, @-, @-,
@, @, @, @, @, @, @, @,
@, @, @, @, @, @, @, @] retain]; int i = ;
for(NSNumber *num in mArray)
mNumbers[i++] = num; return self;
} - (void)dealloc
{
if(mArray != nil)
{
[mArray release];
mArray = nil;
} [super dealloc];
} - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id*)stackbuf count:(NSUInteger)len
{
// 我们用state来表示状态。状态为1返回零,说明迭代结束;若状态为0,则继续迭代
// state初始值为零
if(state->state > )
return ; // mutationsPtr不能为空,如果假定在枚举过程中不发生修改,一般指向self
state->mutationsPtr = (unsigned long*)self; // 我们将当前剩余长度放在state的extra[0]之中
NSUInteger retCount = state->extra[]; // 所要枚举的数组指针首地址;extra[1]起始值为零
state->itemsPtr = &mNumbers[state->extra[]]; // 若为零,说明这是第一次迭代
if(retCount == )
retCount = [mArray count]; // 这里需要判断当前数组长度是否超过了本次枚举所设置的最大长度
if(retCount > len)
{
// 设置extra[0],存放剩余所要枚举的元素个数
state->extra[] = retCount - len; // 设置extra[1],存放后一次枚举起始元素索引
state->extra[] += len; retCount = len;
}
else
{
// 若剩余所要枚举的元素个数小于最大限制个数,则状态变1,使得后一次迭代能直接结束
state->state++;
} // 返回这次所要枚举的数组一共含有多少元素
return retCount;
} @end int main (int argc, const char * argv[])
{
@autoreleasepool
{
// insert code here... MyIterator *it = [[MyIterator alloc] init]; NSLog(@"The elements are: ");
for(NSNumber *num in it)
printf("%ld ", [num integerValue]);
puts("\n"); [it release]; MyFastIterator *iter = [[MyFastIterator alloc] init]; NSLog(@"The elements are: "); for(NSNumber *num in iter)
{
printf("%ld ", [num integerValue]);
}
puts(""); [iter release];
} return ;
}

Objective-C如何自己实现一个for-each语法形式的更多相关文章

  1. Swift和Objective C关于字符串的一个小特性

    一.Unicode的一个小特性 首先,Unicode规定了许多code point,每一个code point表示一个字符.如\u0033表示字符"3",\u864e表示字符&qu ...

  2. ABAP 通过sumbit调用另外一个程序使用job形式执行-简单例子

    涉及到两个程序: ZTEST_ZUMA02 (主程序) ZTEST_ZUMA(被调用的程序,需要以后台job执行) "ztest_zuma 的代码 DATA col TYPE i VALUE ...

  3. 如果以一个树状的形式返回一个UIView的所有子视图

    该方法也是从一个视频中看到,总觉得会有很大作用,故记录在这里. 它返回一个xml的字符串,用火狐浏览器或者其他可以格式化xml的工具打开,即可查看其层级关系. /** * 返回传入view的所有层级结 ...

  4. 用java实现一个简易编译器-语法解析

    语法和解析树: 举个例子看看,语法解析的过程.句子:“我看到刘德华唱歌”.在计算机里,怎么用程序解析它呢.从语法上看,句子的组成是由主语,动词,和谓语从句组成,主语是“我”,动词是“看见”, 谓语从句 ...

  5. c语言打印一个整数的二进制形式

    printf函数没有这个功能,如果想打印一个数的二进制形式,就得自己计算.下面是我看到的最简便的算法: #include <stdio.h> int main(int argc, char ...

  6. JAVA--可变长参数

    可变长参数: 可变长参数可以接受任意个数的实参,形参实际上是一个数组. 语法形式: 方法名称(类型 参数1,类型 参数2,类型...可变长参数) *可变长参数一定是方法的最后一个参数 public v ...

  7. Java Web开发模式

    一 Java Web开发模式的变迁 1 最初的Java web服务器端编程技术是Servlet,利用Servlet就可以开发出一个Web应用程序. 2 为了解决Servlet缺陷,SUN推出了JSP技 ...

  8. NLP领域的ImageNet时代到来:词嵌入「已死」,语言模型当立

    http://3g.163.com/all/article/DM995J240511AQHO.html 选自the Gradient 作者:Sebastian Ruder 机器之心编译 计算机视觉领域 ...

  9. Java 运算符(引用和对象)

    1. 算数运算符 就是+.-.*./.%.++.--这些,没什么好说的,稍微强调下自加,自减: 前缀自增自减法(++i,--i): 先进行自增或者自减运算,再进行表达式运算. 后缀自增自减法(i++, ...

随机推荐

  1. python读取图像后变换通道顺序

    直接通过python矩阵操作变换,简单高效 org_img = cv2.imread('cat.jpg') img = org_img[:, :, ::-1] 其中,[::-1] 表示顺序相反操作 , ...

  2. Visual studio 2010 打开高版本VS工程解决办法

    第一步.找到工程项目文件: 第二步.编辑项目文件 找到Format Version 12.00 ,将数字改为11.00 再找到#Visual Studio 14 将数字改为2010 点击保存并关闭 第 ...

  3. Nginx 优化详解

    一.一般来说nginx 配置文件中对优化比较有作用的为以下几项: 1. worker_processes 8; nginx 进程数,建议按照cpu 数目来指定,一般为它的倍数 (如,2个四核的cpu计 ...

  4. idou老师教你学Istio 16:如何用 Istio 实现微服务间的访问控制

    摘要 使用 Istio 可以很方便地实现微服务间的访问控制.本文演示了使用 Denier 适配器实现拒绝访问,和 Listchecker 适配器实现黑白名单两种方法. 使用场景 有时需要对微服务间的相 ...

  5. 透过字节码生成审视Java动态代理运作机制

    对于动态代理我想应该大家都不陌生,就是可以动态去代理实现某个接口的类来干一些我们自己想要的功能,但是在字节码层面它的表现是如何的呢?既然目前刚好在研究字节码相关的东东,有必要对其从字节码角度来审视一下 ...

  6. nginx 重发机制导致的重复扣款问题

    问题:    nginx 重发机制导制重复提交(客户还款,被扣俩笔款,前端调用一次,后端执行2次) proxy_next_upstream 语法: proxy_next_upstream error ...

  7. WebRequest与WebResponse抽象类,DNS静态类、Ping类

    一.概述 1.WebRequest: 对统一资源标识符 (URI) 发出请求. 这是一个 abstract 类. WebRequest的派生类:PackWebRequest.FileWebReques ...

  8. c语言1博客作业11

    一.本周作业头 这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 http://edu.cnblogs.com/campus/zswxy/SE2019-4/homework/10125 我 ...

  9. request.querystring和request.form的区别

    1,request.querystring和request.form的区别 request.querystring是用来接收地址里面问号“?”后面的参数的内容,  用get方法读取的 不安全 requ ...

  10. linux命令集合(二)

    yum源的配置 yum  得配置yum源,配置阿里云的 两个 yum源  ,阿里云的yum源中,会有 mariadb的软件包   阿里云的yum仓库中,mariadb版本如下 mariadb      ...