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