0

这是我的嵌套块,请看一下:

- (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"];

所以该值没有插入我的对象模型中。请给我一个建议。提前致谢。
祝你有美好的一天。

4

1 回答 1

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 提升到顶部。

当您有一个本身依赖于另一个异步方法的异步方法时,这种异步调用链是基本的——在每个阶段,您必须使用完成块链以正确的顺序执行代码。

高温高压

于 2017-11-12T20:50:56.080 回答