我们在用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. 【Hibernate】Hibernate关联关系的映射

    一.实体之间的关系 二.一对多的配置 2.1 第一步创建两个实体 2.2 第二步:配置映射文件 2.3 第三步:将映射放到核心配置文件中 三.级联 3.1 Hibernate中级联保存的效果 3.2 ...

  2. linux使用glibc版本安装mysql8.0.12

    1.前言 使用yum安装虽然很方便,但是如果要是在没有公网的环境下,是没有办法使用yum源的.所以我们可以使用mysql提供的glibc版本的安装包,进行安装. 但是在安装之前,一定要将以前的版本删除 ...

  3. AWD模式搅屎模式

    AWD模式搅屎模式 ###0x01 出题思路 ####1:题目类型 1-出题人自己写的cms,为了恶心然后加个so. 2-常见或者不常见的cms. 3-一些框架漏洞,比如ph师傅挖的CI这种 #### ...

  4. httpd源码编译安装

    什么是编译安装——编译:将源代码变为机器可执行的代码文件.安装:将可执行文件安装到操作系统里,才可以使用. 一.下载httpd源码包 在官网上下载httpd源码包http://httpd.apache ...

  5. 服务器CPU架构演变过程

    第一阶段: 单CPU,没啥好说的.和我们今天打游戏的台式机差不多. 第二阶段: 双CPU 或者 多CPU的SMP架构 或者 AMP架构.参考:http://www.lauterbach.com/smp ...

  6. jquery中prop,attr,data的区别

    这两天翻了jq的源码,今天看到了jq关于数据存储的几个方法,遂总结一下,和小伙伴没分享一下,哪里说的不对,还望批评指正~~~ 废话不多说,直接上代码: $(function(){ $('#div1') ...

  7. cmd生成大文件

    用cmd生成一个大小一定的文件 输入fsutil file createnew  文件位置  文件大小(以字节为单位1024b=1kb) 列如:fsutil file createnew  d:\my ...

  8. 【Leetcode】【简单】【283. 移动零】【JavaScript】

    题目描述 283. 移动零 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例: 输入: [0,1,0,3,12]输出: [1,3,12,0,0] 说 ...

  9. [GDOI2014]采集资源

    题目概述 题目描述 魔兽争霸3中,战略资源的采集通过使用农民.苦工.小精灵以及寺僧来进行. 在魔兽争霸4的开发中,玻璃渣觉得这种模式太过单一,于是他们想添加更多的单位来使采集的模式更加丰富. 在新的模 ...

  10. Nginx中ngx_http_proxy_module模块

    该模块允许将请求传递给另⼀一台服务器器指令:1 ,proxy_pass设置代理理服务器器的协议和地址以及应映射位置的可选 URI .作为协议,可以指定“ http 或 https .可以将地址指定为域 ...