ios 添加block 类别_使自定义的类支持 iOS 的枚举方式

在 iOS 中主要有 4 种类型的遍历:C 语言风格

NSEnumerator

基于 block 的遍历

NSFastEnumeration

C 语言风格1

2

3

4NSArray *nums = @[@1, @2, @3];

for (int i = 0; i < nums.count, ++i) {

NSLog(@"%@", nums[i]);

}

形如上面这样的利用 for 循环,然后使用下标去访问对象的方式,就是 C 语言风格的遍历。

要支持这种 C 语言风格遍历就需要实现 objectAtIndexedSubscript: 方法,这是因为编译器会将 someArray[0] 解析成 [someArray objectAtIndexedSubscript:0]。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15@interface : NSObject

- (id)objectAtIndexedSubscript:(NSUInteger)idx;

@end

@implementation{

std::vector _numberList;

}

- (id)objectAtIndexedSubscript:(NSUInteger)idx {

if (idx >= _numberList.size()) {

}

return _numberList[idx];

}

@end

如果要支持使用键作为下标来访问值,就需要实现 objectForKeyedSubscript: 方法。

NSEnumerator1

2

3

4

5

6NSArray *numberArray = @[@1, @2, @3];

NSEnumerator *enumerator = [numberArray objectEnumerator];

NSNumber *number;

while (number = [enumerator nextObject]) {

// 对 number 对象进行操作

}

这是 ObjC 原来遍历集合的标准方式,但是这种写法很冗长。虽然现在基本上不用这种方式了,还是来看一下如何支持这种方式的遍历。

要支持这种遍历方式主要还是实现一个 objectEnumerator 方法返回 NSEnumerator 对象。这里的 NSEnumerator 是抽象类,需要继承 NSEnumerator 然后实现 -nextObject: 方法。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47@interface TestEnumerator: NSEnumerator

@property (nonatomic, readonly) TestArray *array;

- (instancetype)initWithTestArray:(TestArray *)array;

@end

@implementation TestEnumerator{

NSUInteger _currentIndex;

}

- (instancetype)initWithTestArray:(TestArray *)array {

if (self = [super init]) {

_array = array;

_currentIndex = 0;

}

return self;

}

- (id)nextObject {

if (_currentIndex >= [self.array numberOfItems]) {

return nil;

} else {

return array[_currentIndex++]; // 假设 TestArray 实现了 `objectAtIndexedSubscript:` 方法

}

}

// 这里还需要注意的是 NSEnumerator 会自动实现 `-allObjects` 方法,将 `-nextObject` 方法返回的对象填入数组中

@end

@interface : NSObject

- (NSEnumerator *)objectEnumerator;

- (NSUInteger)numberOfItems;

@end

@implementation{

std::vector _numberList;

}

- (NSUInteger)numberOfItems {

return _numberList.size();

}

- (NSEnumerator *)objectEnumerator {

return [[TestEnumerator alloc] initWithTestArray:self];

}

@end

NSEnumerator 也是遵守 NSFastEnumeration 协议,所以可以还可以使用 for-in 循环来遍历 NSEnumerator 对象。

基于 block 的遍历1

2

3

4NSArray *array = @[@1, @2, @3];

[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {

NSLog(@"%@", object);

}];

这种遍历是现在经常使用的方式,而且这种方式还提供了很多有用的特性。1- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block

上面这个方法就可以指定 NSEnumerationOptions 参数,可以反向遍历和并行遍历。同时系统会在这个方法的 block 里添加一个 autoreleasepool。

这里不太懂,苹果官方给出的例子也就是简单的实现了 enumerateObjectsUsingBlock: 方法。苹果官方例子:FastEnumerationSample.

NSFastEnumeration1

2

3

4NSArray *array = @[@1, @2, @3];

for (NSNumber *number in array) {

NSLog(@"%@", number);

}

利用 for-in 遍历容器对象也是非常常见的遍历方法,并且这种方式也是最快的,要让自定义的对象支持这种遍历模式还是比较麻烦的。

支持这种遍历模式的对象需要遵守 NSFastEnumeration 协议,并实现方法:1- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;

接下来详细解释下这些参数的含义:state: 这是个 NSFastEnumerationState 的结构体,申明如下:1

2

3

4

5

6

7

8

9

10

11typedef struct {

// 在第一次调用 `countByEnumeratingWithState:objects:count:` 方法时,state 为 0,

// 这个在遍历的时候用不到,可以用来存储额外信息

unsigned long state;

// C 数组,`countByEnumeratingWithState:objects:count:` 方法调用者要去遍历的数组

id *itemsPtr;

// 用来检测遍历期间数组是否被修改

unsigned long *mutationsPtr;

// 存储额外信息

unsigned long extra[5];

} NSFastEnumerationState;stackbuf: countByEnumeratingWithState:objects:count: 方法调用者提供的数组,当我们数据结构是不连续的内存时,需要用到这个数组

len: stackbuf 数组的长度,当使用到 stackbuf 数组时,需要用到 len 来检测

实现 countByEnumeratingWithState:objects:count: 这个方法,还需要挺多要写的,打算另写一篇来说如何实现。

相关链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值