歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Objective-C如何自己實現一個for-each語法形式

Objective-C如何自己實現一個for-each語法形式

日期:2017/3/1 9:20:11   编辑:Linux編程

我們在用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