2017-12-23 13 views
0

서버에서 일부 데이터를로드하지만 실제 데이터가로드되기 전에 루프 (for .. in ..)가 완료됩니다. 주 큐에서 구현 디스패치를 ​​수정하거나 다른 것을 실수로 수정하는 방법은 무엇입니까?차단 기능 ObjC에서 만든 주 디스패치 주 대기열 작업을 수정하는 방법?

[ApiManager getCategoriesifSuccess:^{ 
    [self.sectionArray addObjectsFromArray:[CategoryManager getCats]]; 
    for (CategorySectionModel *mod in [CategoryManager getCats]) { 
     [self.arrayForBool addObject:@"YES"]; 
     [ApiManager getCatalogItemsInCity:currentCity withSection:mod.uid 
           andStart:@"1" andLimit:@"20" ifSuccess:^{ 
            [self.itemsArray setObject:[CatalogItemManager getItems] forKey:mod.uid]; 
            [indicator stopAnimating]; 
            [self.tableView reloadData]; 
            NSLog(@"DONE WITH SECTION:%@", mod.title); 
           } orIfFailed:^(NSString *fail) { 
            NSLog(@"%@", fail); 
           }]; 
     NSLog(@"LOOP"); 
    }; 
} orIfFailed:^{ 
}]; 

그리고이 콘솔 반환하지만이 올바르지 않은 것 같다

2017-12-24 01:17:57.148355+0300 iOS[75679:1805296] LOOP 
2017-12-24 01:17:57.149576+0300 iOS[75679:1805296] LOOP 
2017-12-24 01:17:57.150786+0300 iOS[75679:1805296] LOOP 
2017-12-24 01:17:57.152773+0300 iOS[75679:1805296] LOOP 
2017-12-24 01:17:57.154727+0300 iOS[75679:1805296] LOOP 
2017-12-24 01:17:57.156987+0300 iOS[75679:1805296] LOOP 
2017-12-24 01:17:57.158840+0300 iOS[75679:1805296] LOOP 
2017-12-24 01:17:57.981119+0300 iOS[75679:1805296] DONE WITH SECTION:First 
2017-12-24 01:17:58.285569+0300 iOS[75679:1805296] DONE WITH SECTION:Second 
2017-12-24 01:17:58.403725+0300 iOS[75679:1805296] DONE WITH SECTION:Third 
2017-12-24 01:17:58.434170+0300 iOS[75679:1805296] DONE WITH SECTION:Fourth 
2017-12-24 01:17:58.449970+0300 iOS[75679:1805296] DONE WITH SECTION:Fifth 
2017-12-24 01:17:58.469519+0300 iOS[75679:1805296] DONE WITH SECTION:Sixth 
2017-12-24 01:17:58.535116+0300 iOS[75679:1805296] DONE WITH SECTION:Seventh 

답변

1

사용 파견 그룹 :

스위프트 (의사 코드) :

let group = DispatchGroup() 

    for ... { 
     group.enter() 

     someAsyncMethod({ 
      group.leave() 
     }) 
    } 
    group.notify(queue: DispatchQueue.main, execute: { 
     // you code here will be invoked after all group.leave() 
    }) 

당신을 확인하십시오 각 group.enter()에 대해 하나만 (!) group.leave()을 호출하십시오. 실패 콜백이있는 경우 성공 및 실패 콜백을 호출해야합니다. group.leave()

+0

질문은 Objective-C에 관한 것입니다. –

+1

그것은 나에게 변화가없는 것처럼 보인다. –

+1

@IlyaChikmarev - 아니요, 이것은 중요한 변화입니다. 우리는 모든 요청이 언제 완료되었는지 (어떤 시점에서 당신은 아마도 다른 행동을 유발할 것임을 알기 위해) 당신이 의도 한 것이라고 생각합니다. 디스패치 그룹을 사용하는 것이 올바른 방법입니다 (Alexander가 Objective-C 예제를 제공했으면 좋겠지 만). – Rob

1

이 동작은 의도 한 것이며 비동기 전달을위한 것입니다. 간단한 예제로 감소하자 긴 배경은 초, 분, 시간이 걸릴 수 있습니다 걸리는 동안 메인 제어 흐름의 실행이 즉시 계속 때문에

[Manager doSomethingLongInBackgroundWithCompletionHandler: 
^{ 
    NSLog(@"done"); 
}]; 
NSLog(@"Loop"); 

이 경우, 당신은 첫 번째 루프를 볼 수 있습니다. (이것은 의도 된 행동이기 때문에, 그것은 문제가되지 않습니다)

, 당신은 그것으로 완료 후에 실행되어야하는 코드를 넣어 가지고 그 문제를 해결하려면에서

[Manager doSomethingLongInBackgroundWithCompletionHandler: 
^{ 
    NSLog(@"done"); 
    NSLog(@"Loop"); 
}]; 

당신의 로그가 완료 처리기 내부에서 이동한다는 것을 의미 경우 :

for (CategorySectionModel *mod in [CategoryManager getCats]) { 
    [self.arrayForBool addObject:@"YES"]; 
    [ApiManager getCatalogItemsInCity:currentCity withSection:mod.uid andStart:@"1" andLimit:@"20" ifSuccess: 
    ^{ 
     [self.itemsArray setObject:[CatalogItemManager getItems] forKey:mod.uid]; 
     [indicator stopAnimating]; 
     [self.tableView reloadData]; 
     NSLog(@"DONE WITH SECTION:%@", mod.title); 
     NSLog(@"LOOP"); 
    } 
    orIfFailed: 
    ^(NSString *fail) 
    { 
     NSLog(@"%@", fail); 
     NSLog(@"LOOP"); 
    }]; 
}; 

I 가정, 즉 NSLog(@"LOOP") 더 관련 코드에 대한 자리이다.

1

설명하는 동작이 정확하며 예상해야합니다.

비동기 작업 루프를 수행하고 있습니다. 즉, 비동기 작업을 시작하면서 루핑을 진행하고 있지만 네트워크 요청이 느리게 진행되는 동안 루프가 즉시 완료됩니다. 따라서 모든 네트워크 요청이 언제 완료되었는지 알 수있는 방법이 필요합니다. 예를 들어 indicator을 중지 할 수 있습니다.

대표적인 해결 방법은 디스패치 그룹입니다.

[indicator startAnimating]; 

[ApiManager getCategoriesifSuccess:^{ 
    dispatch_group_t group = dispatch_group_create(); 

    NSArray *categories = [CategoryManager getCats]; 
    [self.sectionArray addObjectsFromArray:categories]; 

    for (CategorySectionModel *mod in categories) { 
     dispatch_group_enter(group); 

     [self.arrayForBool addObject:@"YES"]; 
     [ApiManager getCatalogItemsInCity:currentCity withSection:mod.uid andStart:@"1" andLimit:@"20" ifSuccess:^{ 
      [self.itemsArray setObject:[CatalogItemManager getItems] forKey:mod.uid]; 
      NSLog(@"DONE WITH SECTION:%@", mod.title); 

      // personally I'd reload section by section, e.g. 
      // 
      // NSInteger section = ... 
      // [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationFade]; 

      dispatch_group_leave(group); 
     } orIfFailed:^(NSString *fail) { 
      NSLog(@"%@", fail); 
      dispatch_group_leave(group); 
     }]; 
    }; 

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
     [indicator stopAnimating]; 
     [self.tableView reloadData]; // assuming you didn't just reload section-by-section as they finished 
    }); 
} orIfFailed:^{ 
    [indicator stopAnimating]; 
    // display something about the nature of the error 
}]; 

, 나는 이러한 다양한 방법을 가능성이 동시성을 지원하도록 설계되지 않았 음을, 당신은 더 깊은 문제가 의심이 말한다면 (즉, 같은 시간에 일어나고 여러 비동기 요청이있을 수 있음). 예를 들어 특정 카테고리의 항목을 가져 오는 경우 [CatalogItemManager getItems]으로 전화를 걸 수 있습니다. 그러나 여러 요청이 동시에 진행된다면 작동하지 않습니다. getCatalogItemsInCity을 (a) 항목을 일부 로컬 변수에 유지하도록 변경해야합니다. (b) success 블록의 매개 변수로 다시 전달합니다.

(마찬가지로 getCategoriesifSuccess 아마 당신이 범주)가 동시에 진행에 대한 여러 쿼리가없는 의심 때문에이 (가 덜 중요합니다. [CategoryManager getCats]에 의존하지, 그 success 블록에 매개 변수로 범주를 통과해야하지만 아직 더 나은 디자인.)처럼 보일 수 있습니다

: 당신의 완료 핸들러가 다시 메인 큐 여부를 파견하는 경우

[ApiManager getCategoriesIfSuccess:^(NSArray *categories){ 
    dispatch_group_t group = dispatch_group_create(); 

    [self.sectionArray addObjectsFromArray:categories]; 

    for (CategorySectionModel *mod in categories) { 
     dispatch_group_enter(group); 

     [self.arrayForBool addObject:@"YES"]; 
     [ApiManager getCatalogItemsInCity:currentCity withSection:mod.uid andStart:@"1" andLimit:@"20" ifSuccess:^(NSArray *items){ 
      [self.itemsArray setObject:items forKey:mod.uid]; 
      NSLog(@"DONE WITH SECTION:%@", mod.title); 

      NSInteger section = ... 
      [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationFade]; 

      dispatch_group_leave(group); 
     } orIfFailed:^(NSString *fail) { 
      NSLog(@"%@", fail); 
      dispatch_group_leave(group); 
     }]; 
    }; 

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
     [indicator stopAnimating]; 
    }); 
} orIfFailed:^{ 
    [indicator stopAnimating]; 
    // display something about the nature of the error 
}]; 

은 BTW, 나도 몰라. 위와 같이 가정했지만, 그렇지 않은 경우 모든 모델 및 UI 업데이트를 기본 대기열로 수동으로 보내야합니다.

+0

정말 좋은 설명과 당신의 생각에 옳습니다! 이제는 저에게 더 분명합니다. 그게 도움이 많이! –