这是我的嵌套块,请看看:
- (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"];
因此该值未插入到我的对象模型中。
请给我一个建议。提前谢谢。
祝你有个美好的一天。
答案 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