刚遇到这个问题,还是有点摸不着头脑,说不出这样设计是出于怎么考虑,当然,设计初衷无非就是用指针对象更容易容易实现xx 功能,避免 xx bug等等,但只这样说没有实践过是无法作为呈堂供词的。
遇到这样的问题可以反过来考虑,如果让自己来实现类似 NSArray
的 enumerateObjectsUsingBlock
的实例方法,那么该怎么设计呢?
为了不影响 NSArray
原有的方法,自己加个 category
。
先写一个简单点不考虑 stop
的方法:
- (void)xx_enumerateObjectsUsingBlockWithoutStop:(void (^)(id obj, NSUInteger idx))block;
实现方法简单点(先省去各种判断)
- (void)xx_enumerateObjectsUsingBlockWithoutStop:(void (^)(id obj, NSUInteger idx))block {
int i = 0;
for (id tempObject in self) {
if (block) {
block(tempObject, i);
}
i++;
}
}
随便写个类验证一下:
NSArray *array = @[@"ABC",
@"DEF",
@"GHI",
@"JKL",
@"MNO",
@"PQR"];
[array xx_enumerateObjectsUsingBlockWithoutStop:^(id _Nonnull obj, NSUInteger idx) {
NSLog(@"%ld -- %@", (long)idx, obj);
}];
2019-07-28 22:53:43.850316+0800 HaveFun[910:41332] 0 -- ABC
2019-07-28 22:53:43.850451+0800 HaveFun[910:41332] 1 -- DEF
2019-07-28 22:53:43.850515+0800 HaveFun[910:41332] 2 -- GHI
2019-07-28 22:53:43.850577+0800 HaveFun[910:41332] 3 -- JKL
2019-07-28 22:53:43.850642+0800 HaveFun[910:41332] 4 -- MNO
2019-07-28 22:53:43.850697+0800 HaveFun[910:41332] 5 -- PQR
现在考虑 stop
的情况,stop
的作用就是终止 for
循环,表现出来的应该类似于
for (id tempObject in self) {
/*
循环体
*/
if (stop) {
break;
}
}
如果在不了解 enumerateObjectsUsingBlock
方法之前实现类似功能,可能会写出这样的代码,(当然,规范来说,一般会将block
写在参数列表的最后面)
- (void)xx_enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx))block stopIndex:(NSInteger)index {
int i = 0;
for (id tempObject in self) {
if (block) {
block(tempObject, i);
}
if (index == i) {
break;
}
i++;
}
}
这样写虽然实现了指定条件下的终止循环,但并没有什么实际意义,试想一下,如果已经知道需要的遍历次数,那么直接缩小遍历范围就可以,没必要多此一举。需求的目的是要在遍历过程中如果找到了目标值,那么接下来的遍历就不需要了。例如:查找一次考试中姓名是 “小满” 的同学的成绩,(这里假定姓名唯一),遍历之前并不晓得名字是 “小满”是在列表中的顺序,可能是第一位,也可能是最后一位,但只要满足
if (array[i].name == @"小满") {
NSLog(@"成绩: %lf", array[i].score);
break;
}
这样列表后面的同学成绩就不需要查找了,可以有效的降低遍历次数,达到节能减排的目的。
走一波原方法
NSArray *array = @[@"ABC",
@"DEF",
@"GHI",
@"JKL",
@"MNO",
@"PQR"];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (3 == idx) {
*stop = YES;
}
NSLog(@"%ld -- %@, %@", (long)idx, obj, *stop == YES ? @"YES":@"NO");
}];
2019-07-28 23:37:25.791822+0800 HaveFun[1286:80311] 0 -- ABC, NO
2019-07-28 23:37:25.792015+0800 HaveFun[1286:80311] 1 -- DEF, NO
2019-07-28 23:37:25.792062+0800 HaveFun[1286:80311] 2 -- GHI, NO
2019-07-28 23:37:25.792123+0800 HaveFun[1286:80311] 3 -- JKL, YES
当 *stop = YES;
时,会执行完这次 block
中的循环,下次循环就不再执行了。
这里需要的是在每次循环的过程中,重新获取下是否需要终止循环
尝试着根据原 api
来(简单)实现一波
- (void)xx_enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop)) block {
// 终止标志
BOOL stopFlag = NO;
for (NSUInteger i=0; i
调用方式是一样的
[array xx_enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (2 == idx) {
*stop = YES;
}
NSLog(@"%ld -- %@ ", (long)idx, obj);
}];
2019-07-29 09:40:38.963120+0800 HaveFun[984:30399] 0 -- ABC
2019-07-29 09:40:38.963290+0800 HaveFun[984:30399] StopFlag === 0
2019-07-29 09:40:38.963381+0800 HaveFun[984:30399] 1 -- DEF
2019-07-29 09:40:38.963474+0800 HaveFun[984:30399] StopFlag === 0
2019-07-29 09:40:38.963592+0800 HaveFun[984:30399] 2 -- GHI
2019-07-29 09:40:38.963676+0800 HaveFun[984:30399] StopFlag === 1
如果把 Bool *stop
替换为你 Bool stop
- (void)xx_enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL stop)) block {
// 终止标志
BOOL stopFlag = NO;
for (NSUInteger i=0; i
调用
[array xx_enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL stop) {
if (2 == idx) {
stop = YES;
}
NSLog(@"%ld -- %@ ", (long)idx, obj);
}];
很遗憾
2019-07-29 10:06:02.963879+0800 HaveFun[1211:53383] 0 -- ABC
2019-07-29 10:06:02.964073+0800 HaveFun[1211:53383] StopFlag === 0
2019-07-29 10:06:02.964157+0800 HaveFun[1211:53383] 1 -- DEF
2019-07-29 10:06:02.964235+0800 HaveFun[1211:53383] StopFlag === 0
2019-07-29 10:06:02.964310+0800 HaveFun[1211:53383] 2 -- GHI
2019-07-29 10:06:02.964384+0800 HaveFun[1211:53383] StopFlag === 0
2019-07-29 10:06:02.964456+0800 HaveFun[1211:53383] 3 -- JKL
2019-07-29 10:06:02.964528+0800 HaveFun[1211:53383] StopFlag === 0
2019-07-29 10:06:02.964600+0800 HaveFun[1211:53383] 4 -- MNO
2019-07-29 10:06:02.964764+0800 HaveFun[1211:53383] StopFlag === 0
2019-07-29 10:06:02.964986+0800 HaveFun[1211:53383] 5 -- PQR
2019-07-29 10:06:02.965202+0800 HaveFun[1211:53383] StopFlag === 0
说明 Bool stop
并不能控制循环的终止条件。
我们知道指针操作都是对物理内存的操作,把 stopFlag
的内存地址 &stopFlag
传给调用者, 那么调用者就可以与实现函数共享 stopFlag
变量。根据猜想,代码稍作修改,验证下:
在实现和调用函数中都加入当前 stop
地址的log
NSLog(@"StopFlag === %d, Addr = %p", stopFlag, &stopFlag); // 实现函数中加入
NSLog(@"%ld -- %@, SubAddr: %p", (long)idx, obj, &stop); //调用函数中加入
2019-07-29 10:14:58.928249+0800 HaveFun[1293:60577] 0 -- ABC, SubAddr: 0x7ffeebe91137
2019-07-29 10:14:58.928392+0800 HaveFun[1293:60577] StopFlag === 0, Addr = 0x7ffeebe911a7
...
...
可见根本不是同一个变量,所以无论调用者怎么改变 stop
的值都是无济于事的。
由此可见,若想实现调用者与函数实现共享内存的方法,需要使用一级指针。