2013-01-12 44 views
1

我使用快速枚舉,並在枚舉塊I中異步發送網絡請求。NSArray enumerateObjectsUsingBlock:延遲枚舉

所以會發生什麼是enumerateObjectsUsingBlock:只要調用塊超快速並讓枚舉塊在一段時間後完成。

這導致了不同的結果,因爲一些請求比其他請求更快完成。所以它沒有按照我的意願排序。

有什麼辦法可以將塊凍結,並且在異步網絡請求完成之後,只是告訴它去下一個?

下面是一些代碼

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); 
     }]; 
    }]; 

謝謝!

+0

快速枚舉和枚舉使用塊是不一樣的。請發佈一些上下文的代碼,它會更容易幫助你 – Stavash

+0

謝謝,現在編輯這個問題! – Devfly

+0

異步網絡請求怎麼樣?他們在哪裏進來? – Stavash

回答

0

有沒有什麼方法可以將塊凍結,並且在異步網絡請求完成之後,只是告訴它去下一個?

您可以通過重新組織你的代碼達到這種效果:而不是使用枚舉,只執行從完成塊的異步請求,一個在時間:

- (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)。

或者您可能會實施Async Completion Token pattern,以便能夠在接收到響應時對其進行重新排序。

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秒延遲。您可以在塊內進行網絡調用,然後在完成後再調用。如果您遇到錯誤並想取消設置,請按* 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(); 
    }]; 
}]; 
1

有什麼辦法來設置凍結塊,和 異步網絡請求完成後,直接告訴它去 下一個?

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); 
    } 
});