歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

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("\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 0;
}

Objective-C中@property的所有屬性詳解 http://www.linuxidc.com/Linux/2014-03/97744.htm

Objective-C 和 Core Foundation 對象相互轉換的內存管理總結 http://www.linuxidc.com/Linux/2014-03/97626.htm

使用 Objective-C 一年後我對它的看法 http://www.linuxidc.com/Linux/2013-12/94309.htm

10個Objective-C基礎面試題,iOS面試必備 http://www.linuxidc.com/Linux/2013-07/87393.htm

Objective-C適用C數學函數 <math.h> http://www.linuxidc.com/Linux/2013-06/86215.htm

好學的 Objective-C 高清PDF http://www.linuxidc.com/Linux/2014-09/106226.htm

Copyright © Linux教程網 All Rights Reserved