Вложенное завершение 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 на вершину.
Такая цепочка асинхронных вызовов является фундаментальной, когда у вас есть асинхронный метод, который сам опирается на другой асинхронный метод - на каждом этапе вы должны использовать цепочку блоков завершения, чтобы выполнить код в правильном порядке.
НТН