zoukankan      html  css  js  c++  java
  • Objective-C如何自己实现一个for-each语法形式

    我们在用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[5];    // 这里可以由实现者随意存放必要的额外数据
    } 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 = [@[@1, @2, @3, @4, @5, @6, @7, @8] 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[100];
    }
    
    @end
    
    @implementation MyFastIterator
    
    - (instancetype)init
    {
        self = [super init];
        
        // 先对mArray进行初始化
        mArray = [@[@100, @200, @300, @400, @-1, @-2, @-3, @-4,
                    @1, @2, @3, @4, @5, @6, @7, @8,
                    @11, @12, @13, @14, @15, @16, @17, @18] retain];
        
        int i = 0;
        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 > 0)
            return 0;
        
        // mutationsPtr不能为空,如果假定在枚举过程中不发生修改,一般指向self
        state->mutationsPtr = (unsigned long*)self;
        
        // 我们将当前剩余长度放在state的extra[0]之中
        NSUInteger retCount = state->extra[0];
        
        // 所要枚举的数组指针首地址;extra[1]起始值为零
        state->itemsPtr = &mNumbers[state->extra[1]];
        
        // 若为零,说明这是第一次迭代
        if(retCount == 0)
            retCount = [mArray count];
        
        // 这里需要判断当前数组长度是否超过了本次枚举所设置的最大长度
        if(retCount > len)
        {
            // 设置extra[0],存放剩余所要枚举的元素个数
            state->extra[0] = retCount - len;
            
            // 设置extra[1],存放后一次枚举起始元素索引
            state->extra[1] += 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("
    ");
            
            [it release];
            
            MyFastIterator *iter = [[MyFastIterator alloc] init];
            
            NSLog(@"The elements are: ");
            
            for(NSNumber *num in iter)
            {
                printf("%ld  ", [num integerValue]);
            }
            puts("");
            
            [iter release];
        }
        
        return 0;
    }
  • 相关阅读:
    es6之更优雅的条件语句
    html 提取 公用部分
    jQuery 新添加元素事件绑定无效
    关于ie6块元素行内元素转换
    git 入门级使用
    vim入门级使用
    git安装配置
    学习使用mac
    Angular常用标记
    npm/bower/brew
  • 原文地址:https://www.cnblogs.com/zenny-chen/p/3593106.html
Copyright © 2011-2022 走看看