Вложенное завершение Block не вызывается objc

Это мой вложенный блок, пожалуйста, взгляните:

- (void)getVideoList:(NSDictionary*)videoData
     completionBlock:(void (^)(NSMutableArray *))
completionBlock {
    NSArray *videos = (NSArray*)[videoData objectForKey:@"items"];
    NSMutableArray* videoList = [[NSMutableArray alloc] init];

    for (NSDictionary *videoDetail in videos) {
        if (videoDetail[@"id"][@"videoId"]){
            [self initializeDictionary:videoDetail completionBlock:^(YoutubeVideo * utubeVideo) {
                [videoList addObject:utubeVideo];
//                NSLog(@"zuuudo %@", utubeVideo.channelProfileImageURL);
            }];
        }
    }
    completionBlock(videoList);
}

- (void)initializeDictionary:(NSDictionary *)dictionary completionBlock:(void (^)(YoutubeVideo *))
completionBlock {
    YoutubeVideo *youtubeVideo = [[YoutubeVideo alloc] init];

    youtubeVideo.videoTitle = dictionary[@"snippet"][@"title"];
    youtubeVideo.videoID = dictionary[@"id"][@"videoId"];
    youtubeVideo.channelID = dictionary[@"snippet"][@"channelId"];
    [self getChannelProfilePictureForChannelID:youtubeVideo.channelID completionBlock:^(NSMutableArray *channelList) {
        NSLog(@"[channelList objectAtIndex:0] %@", [channelList objectAtIndex:0]);
        youtubeVideo.channelProfileImageURL = [channelList objectAtIndex:0];
    }];
    youtubeVideo.channelTitle = dictionary[@"snippet"][@"channelTitle"];
    youtubeVideo.videoDescription = dictionary[@"snippet"][@"description"];
    youtubeVideo.pubDate = [self dateWithJSONString:dictionary[@"snippet"][@"publishedAt"]];
    youtubeVideo.thumbnailURL = dictionary[@"snippet"][@"thumbnails"]
    [@"high"][@"url"];
    completionBlock(youtubeVideo);
}

- (void)getChannelProfilePictureForChannelID:(NSString*)channelID completionBlock:(void (^)(NSMutableArray *))completionBlock
{
    NSString *URL = [NSString stringWithFormat:@"https://www.googleapis.com/youtube/v3/channels?part=snippet&fields=items/snippet/thumbnails/default&id=%@&key=%@", channelID, apiKey];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:[URL stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]]];

    NSURLSession *session = [NSURLSession sharedSession];
    [[session dataTaskWithRequest:request
                completionHandler:^(NSData *data,
                                    NSURLResponse *response,
                                    NSError *error) {
                    if (!error){
                        [self getChannelProfileImageList:[NSJSONSerialization JSONObjectWithData:data options:0 error:nil] completionBlock:
                         ^(NSMutableArray * channelList) {
                             // return the final list
                             completionBlock(channelList);
                         }];
                    }
                    else {
                        // TODO: better error handling
                        NSLog(@"error = %@", error);
                    }
                }] resume];
}

- (void)getChannelProfileImageList:(NSDictionary*)channelData
     completionBlock:(void (^)(NSMutableArray *))
completionBlock {
    NSArray *channels = (NSArray*)[channelData objectForKey:@"items"];
    NSMutableArray *channelList = [[NSMutableArray alloc] init];

    for (NSDictionary *channelDetail in channels) {
        [self initializeDictionaryForChannelProfileImage:channelDetail completionBlock:^(NSString *chnlProfileImageURL) {
            [channelList addObject:chnlProfileImageURL];
        }];
        //[channelList addObject:[self initializeDictionaryForChannelProfileImage:channelDetail]];
        //[channelList addObject:[[YoutubeVideo alloc] initWithDictionaryForChannelProfileImage:channelDetail]];
    }
    completionBlock(channelList);
}

- (void)initializeDictionaryForChannelProfileImage:(NSDictionary *)dictionary completionBlock:(void (^)(NSString *))
completionBlock
{
    _channelProfileImageURL = dictionary[@"snippet"][@"thumbnails"]
    [@"default"][@"url"];

    completionBlock(_channelProfileImageURL);
}

Проблема в этом - (void)initializeDictionary:(NSDictionary *)dictionary completionBlock:(void (^)(YoutubeVideo *)) completionBlock { } блок, имеет блок ниже

[self getChannelProfilePictureForChannelID:youtubeVideo.channelID completionBlock:^(NSMutableArray *channelList) { NSLog(@"[channelList objectAtIndex:0] %@", [channelList objectAtIndex:0]); youtubeVideo.channelProfileImageURL = [channelList objectAtIndex:0]; }];

Где эти строки кода не выполняются, когда блок возвращает значение NSSting значение.

youtubeVideo.channelProfileImageURL = _channelProfileImageURL;
NSLog(@"youtubeVideo.channelProfileImageURL %@", youtubeVideo.channelProfileImageURL);

Он вызывается после выполнения остальной части кода:

    youtubeVideo.channelTitle = dictionary[@"snippet"][@"channelTitle"];
    youtubeVideo.videoDescription = dictionary[@"snippet"][@"description"];
    youtubeVideo.pubDate = [self dateWithJSONString:dictionary[@"snippet"][@"publishedAt"]];
    youtubeVideo.thumbnailURL = dictionary[@"snippet"][@"thumbnails"]
    [@"high"][@"url"];

Таким образом, значение не вставляется в мою объектную модель. Пожалуйста, дайте мне предложение. Заранее спасибо.
Хорошего дня.

1 ответ

Он вызывается после выполнения остальной части кода

Вы смешиваете асинхронное выполнение с ожиданием, что код будет выполняться синхронно:

- (void)initializeDictionary:(NSDictionary *)dictionary 
             completionBlock:(void (^)(YoutubeVideo *))completionBlock
{

Это типичное объявление для асинхронного метода, где completionBlock Аргумент должен быть вызван асинхронно после всей работы initializeDictionary завершено.

    YoutubeVideo *youtubeVideo = [[YoutubeVideo alloc] init];

    youtubeVideo.videoTitle = dictionary[@"snippet"][@"title"];
    youtubeVideo.videoID = dictionary[@"id"][@"videoId"];
    youtubeVideo.channelID = dictionary[@"snippet"][@"channelId"];

Три синхронных задания.

    [self getChannelProfilePictureForChannelID:youtubeVideo.channelID 
                               completionBlock:^(NSMutableArray *channelList)
       {
          NSLog(@"[channelList objectAtIndex:0] %@", [channelList objectAtIndex:0]);
          youtubeVideo.channelProfileImageURL = [channelList objectAtIndex:0];
       }
    ];

Это вложенный вызов другого асинхронного метода, который после завершения вызовет свой блок завершения. В тот момент, когда он возвращается, он, вероятно, еще не назвал свой конкурентный блок.

    youtubeVideo.channelTitle = dictionary[@"snippet"][@"channelTitle"];
    youtubeVideo.videoDescription = dictionary[@"snippet"][@"description"];
    youtubeVideo.pubDate = [self dateWithJSONString:dictionary[@"snippet"][@"publishedAt"]];
    youtubeVideo.thumbnailURL = dictionary[@"snippet"][@"thumbnails"]
    [@"high"][@"url"];

Еще четыре синхронных задания...

    completionBlock(youtubeVideo);

И тогда вы вызываете блок завершения initializeDictionary: прежде чем ты это знаешь getChannelProfilePictureForChannelID: завершил и вызвал свой блок завершения.

}

Если вы пишете асинхронный метод, который сам должен вызывать асинхронный метод, то вы должны завершить свой метод в завершении вложенного асинхронного метода...

Да, это немного запутанно в словах! Давайте изменим ваш метод:

- (void)initializeDictionary:(NSDictionary *)dictionary 
             completionBlock:(void (^)(YoutubeVideo *))completionBlock
{
    YoutubeVideo *youtubeVideo = [[YoutubeVideo alloc] init];

    youtubeVideo.videoTitle = dictionary[@"snippet"][@"title"];
    youtubeVideo.videoID = dictionary[@"id"][@"videoId"];
    youtubeVideo.channelID = dictionary[@"snippet"][@"channelId"];

    youtubeVideo.channelTitle = dictionary[@"snippet"][@"channelTitle"];
    youtubeVideo.videoDescription = dictionary[@"snippet"][@"description"];
    youtubeVideo.pubDate = [self dateWithJSONString:dictionary[@"snippet"][@"publishedAt"]];
    youtubeVideo.thumbnailURL = dictionary[@"snippet"][@"thumbnails"]
    [@"high"][@"url"];

Сначала выполните все синхронные назначения, затем выполните вложенный асинхронный вызов:

    [self getChannelProfilePictureForChannelID:youtubeVideo.channelID 
                               completionBlock:^(NSMutableArray *channelList)
       {
          NSLog(@"[channelList objectAtIndex:0] %@", [channelList objectAtIndex:0]);
          youtubeVideo.channelProfileImageURL = [channelList objectAtIndex:0];

На данный момент блок завершения getChannelProfilePictureForChannelID сделал то, что вы хотите, теперь сделайте любую оставшуюся работу, которая initializeDictionary: нужно сделать после getChannelProfilePictureForChannelID завершается. Это не так много в этом случае, просто позвоните initializeDictionary: соревнование:

          completionBlock(youtubeVideo);
       }
    ];
}

НТН

добавление

Из ваших комментариев я думаю, что вы неправильно понимаете, как асинхронное сцепление должно работать. Посмотрим, поможет ли следующее.

Метод, который вы написали, имеет для формата:

A - block of work to do before async nested call
B - async call
   nested async completion block
   C - block of work to do after nested call completes
D - second block of work
E - call our async completion block

При вызове этого метода A, B, D & E будут выполняться по порядку, а затем метод вернется. Вы не представляете, когда будет выполняться C, и нет никаких гарантий, что он будет выполняться до E, действительно, при асинхронных сетевых вызовах, по всей вероятности, этого не произойдет (поэтому вы вряд ли даже получите случайную корректность).

Для выполнения последовательности асинхронных операций вам нужно связать их в цепочки продолжения. Таким образом, вы можете изменить свой метод на:

A - block of work to do before async nested call
B - async call
   nested async completion block
   C - block of work to do after nested call completes
   D - second block of work
   E - call our async completion block

Помещение D & E во вложенный блок завершения. Теперь, когда вы вызываете ваш метод, только A & B выполняется до его возврата. В какой-то более поздний момент вложенный блок завершения выполняется асинхронно, а C и D выполняются. Наконец E выполняется, завершение блока исходного вызова, тем самым завершая работу. Теперь вы гарантировали правильность, E будет выполняться только после завершения вложенного асинхронного вызова.

Примечание. При чтении вашего кода я заметил, что блок D (набор из четырех назначений в вашем коде), по-видимому, не требуется выполнять после вложенного вызова, поэтому я переставил ваш код следующим образом:

A & D - block of work to do before async nested call
B - async call
   nested async completion block
   C - block of work to do after nested call completes
   E - call our async completion block

поднимая D на вершину.

Такая цепочка асинхронных вызовов является фундаментальной, когда у вас есть асинхронный метод, который сам опирается на другой асинхронный метод - на каждом этапе вы должны использовать цепочку блоков завершения, чтобы выполнить код в правильном порядке.

НТН

Другие вопросы по тегам