嵌套的completionBlock没有被称为objc

时间:2017-11-12 20:21:59

标签: objective-c thread-safety grand-central-dispatch completion-block

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

- (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 个答案:

答案 0 :(得分: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);
       }
    ];
}

HTH

<强>附录

从您的评论中我认为您误解了异步链接需要如何工作。让我们看看以下是否有帮助。

您编写的方法有格式:

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&amp; 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&amp; E进入嵌套的完成块。现在当你打电话给你的方法只有A&amp; 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提升到顶部。

当你拥有一个本身依赖于另一个异步方法的异步方法时,这种异步调用的链接是至关重要的 - 在每个阶段你必须使用完成块链以正确的顺序执行代码。

HTH