2

我使用快速枚举并在枚举块中异步发送网络请求。

因此发生的是 enumerateObjectsUsingBlock:只需超快速地调用该块并让枚举块在一段时间后完成。

这会导致不同的结果,因为某些请求比其他请求完成得更快。所以它没有按我想要的方式排序。

有什么办法可以将block设置为freeze,在异步网络请求完成后,告诉它去下一个吗?

这是一些代码

    NSArray *sites = [self.fetchedResultsController.fetchedObjects copy];
    NSLog(@"sites - %@",sites);
    [sites enumerateObjectsUsingBlock:^(Sites *site, NSUInteger idx, BOOL *stop) {
        NSLog(@"site name - %@,",site.name);

        [[Wrapper sharedWrapper] sendRequestTo:site completionBlock:{

                NSLog(@"site name - %@",site.name);
        }];
    }];

谢谢!

4

3 回答 3

2

有什么办法可以将block设置为freeze,在异步网络请求完成后,告诉它去下一个吗?

NSArray *sites = [self.fetchedResultsController.fetchedObjects copy];
NSLog(@"sites - %@",sites);
[sites enumerateObjectsUsingBlock:^(Sites *site, NSUInteger idx, BOOL *stop) {
    NSLog(@"site name - %@,",site.name);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [[Wrapper sharedWrapper] sendRequestTo:site completionBlock:{

            NSLog(@"site name - %@",site.name);
dispatch_semaphore_signal(semaphore);
    }];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}];

^ 并不是真正的理想方式,但如果您想在等待异步请求完成的同时进行同步迭代,然后再继续前进,上述将通过 GCD 完成。还有其他方法可以在等待异步任务完成后留下所有组的同时迭代和增加 dispatch_group,例如:

dispatch_group_t downloadGroup = dispatch_group_create();


        dispatch_group_enter(downloadGroup);
        [self fetchStuffInBackground:background withCompletion:^(NSArray *stuff, NSError *error) {
            NSLog(@"leaving stuff");
            dispatch_group_leave(downloadGroup);
        }];
        dispatch_group_enter(downloadGroup);
        [self fetchAOtherStuffInBackground:background withCompletion:^(NSArray *stuff, NSError *error) {
            NSLog(@"leaving other stuff");
            dispatch_group_leave(downloadGroup);
        }];
              dispatch_group_enter(downloadGroup);
        [self fetchLastStuffInBackground:background withCompletion:^(NSArray *lastStuff, NSError *error) {
            NSLog(@"leaving last stuff");
            dispatch_group_leave(downloadGroup);
        }];      
    }

}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
    if (callback) {
        callback(error);
    }
});
于 2016-01-04T23:16:25.773 回答
1

我想实现同样的目标,但继续使用块来简化我的代码,而不是通过递归方法传递参数的麻烦。我想出了这个 NSArray 类别:

NS_ASSUME_NONNULL_BEGIN

@interface NSArray(MH)

- (void)mh_asyncEnumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL* stop, dispatch_block_t next))block;

@end

NS_ASSUME_NONNULL_END

@implementation NSArray(MH)

- (void)mh_asyncEnumerateObjectsUsingBlock:(void (^)(id  _Nonnull obj, NSUInteger idx, BOOL* stop, dispatch_block_t next))block{
    __block NSUInteger index = 0;
    __block BOOL stop = NO;

    void (^next)();
    __block __weak typeof(next) weakNext;
    weakNext = next = ^void() {
        void (^strongNext)() = weakNext;
        // check if finished
        if(stop || index == self.count){
            return;
        }
        id obj = self[index];
        index++;
        block(obj, index - 1, &stop, strongNext);
    };
    next();
}

@end

它是这样使用的:

NSArray* a = @[@"Malc", @"Bob", @"Jim"];
[a mh_asyncEnumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL *stop, dispatch_block_t next) {
    // simulate an async network call
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"%@", obj);
        next();
    });
}];

输出:

2016-01-04 22:41:04.631 Malc
2016-01-04 22:41:05.632 Bob
2016-01-04 22:41:06.720 Jim

如您所见,此演示以 1 秒的延迟输出数组中的每个字符串。您可以通过在块内进行网络调用来使用它,然后在完成后调用 next。如果您遇到错误并想取消设置 *stop = YES; 在调用 next() 之前,与普通枚举一样。

NSArray *sites = [self.fetchedResultsController.fetchedObjects copy];
NSLog(@"sites - %@",sites);
[sites mh_asyncEnumerateObjectsUsingBlock:^(Site *site, NSUInteger idx, BOOL *stop, dispatch_block_t next){
    NSLog(@"site name - %@,",site.name);

    [[Wrapper sharedWrapper] sendRequestTo:site completionBlock:{
          if(error){ // your completion block should have an error param!!!
              *stop = YES;
          }
          NSLog(@"site name - %@",site.name);
          next();
     }];
 }];
于 2016-01-04T22:36:10.737 回答
0

有什么办法可以将block设置为freeze,在异步网络请求完成后,告诉它去下一个吗?

您可以通过重新组织代码来实现该结果:而不是使用枚举,只需从完成块执行异步请求,一次一个:

- (void) doRequestAsync:(NSArray*)sites index:(NSUInteger)index {

    if (index >= [sites count]) return;

    NSString* site = [sites objectAtIndex:index];
    [[Wrapper sharedWrapper] sendRequestTo:site completionBlock:{
            NSLog(@"site name - %@",site.name);
            [self doRequestAsync:sites index:++index];
    }];

}

对此的替代方法是修改您的Wrapper类,使其使用异步网络(但在辅助线程上使用它,以避免阻塞 UI)。

或者您可以实现异步完成令牌模式,以便能够在收到响应时重新排序。

于 2013-01-12T10:17:12.073 回答