我有以下使用块和完成处理程序的方法:
// Returns YES if photo is stored in a virtual vacation.
- (BOOL) photoIsOnVacation
{
__block BOOL photoOnFile = NO;
// Identify the documents folder URL.
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSError *errorForURLs = nil;
NSURL *documentsURL = [fileManager URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:&errorForURLs];
if (documentsURL == nil) {
NSLog(@"Could not access documents directory\n%@", [errorForURLs localizedDescription]);
} else {
// Retrieve the vacation stores on file.
NSArray *keys = [NSArray arrayWithObjects:NSURLLocalizedNameKey, nil];
NSArray *vacationURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:documentsURL
includingPropertiesForKeys:keys
options:NSDirectoryEnumerationSkipsHiddenFiles
error:nil];
if (!vacationURLs) photoOnFile = NO;
else {
// Search each virtual vacation for the photo.
for (NSURL *vacationURL in vacationURLs) {
NSError *errorForName = nil;
NSString *vacationName = nil;
[vacationURL getResourceValue:&vacationName forKey:NSURLNameKey error:&errorForName];
[VacationHelper openVacationWithName:vacationName usingBlock:^(UIManagedDocument *vacationDocument) {
NSError *error = nil;
NSManagedObjectContext *moc = vacationDocument.managedObjectContext;
// Build fetch request.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
NSString *currentPhotoID = [self.chosenPhoto objectForKey:FLICKR_PHOTO_ID];
request.predicate = [NSPredicate predicateWithFormat:@"unique = %@", currentPhotoID];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"unique" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
// Execute fetch request.
NSArray *checkPhotos = [moc executeFetchRequest:request error:&error];
if (error) {
NSLog(@"Error searching for photo:%@",error);
} else {
Photo *checkPhoto = [checkPhotos lastObject];
if ([checkPhoto.unique isEqualToString:currentPhotoID]) photoOnFile = YES;
}
}];
if (photoOnFile) break;
}
}
}
return photoOnFile;
}
我的问题是photoOnFile总是假的,因为执行到达包含获取请求的块之前的返回。我已经尝试在dispatch_async中嵌入photoOnFile赋值(dispatch_get_main_queue(),^ {但这没有帮助。任何指导都赞赏。
更新:这里是重写的代码,成功地整合了肯推荐的解决方案:
- (void)checkIfPhotoIsOnVacationAndDo:(void(^)(BOOL photoIsOnVacation))completionBlock
{
__block BOOL photoIsOnVacation = NO;
// Identify the documents folder URL.
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSError *errorForURLs = nil;
NSURL *documentsURL = [fileManager URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:&errorForURLs];
if (documentsURL == nil) {
NSLog(@"Could not access documents directory\n%@", [errorForURLs localizedDescription]);
} else {
// Retrieve the vacation stores on file.
NSArray *keys = [NSArray arrayWithObjects:NSURLLocalizedNameKey, nil];
NSArray *vacationURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:documentsURL
includingPropertiesForKeys:keys
options:NSDirectoryEnumerationSkipsHiddenFiles
error:nil];
if (!vacationURLs) photoIsOnVacation = NO;
else {
// Search each virtual vacation for the photo.
for (NSURL *vacationURL in vacationURLs) {
NSError *errorForName = nil;
NSString *vacationName = nil;
[vacationURL getResourceValue:&vacationName forKey:NSURLNameKey error:&errorForName];
[VacationHelper openVacationWithName:vacationName usingBlock:^(UIManagedDocument *vacationDocument) {
NSError *error = nil;
NSManagedObjectContext *moc = vacationDocument.managedObjectContext;
// Build fetch request.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
NSString *currentPhotoID = [self.chosenPhoto objectForKey:FLICKR_PHOTO_ID];
request.predicate = [NSPredicate predicateWithFormat:@"unique = %@", currentPhotoID];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"unique" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
// Execute fetch request.
NSArray *checkPhotos = [moc executeFetchRequest:request error:&error];
if (error) {
NSLog(@"Error searching for photo:%@",error);
} else {
Photo *checkPhoto = [checkPhotos lastObject];
if ([checkPhoto.unique isEqualToString:currentPhotoID]) {
photoIsOnVacation = YES;
completionBlock(photoIsOnVacation);
}
}
}];
if (photoIsOnVacation) break;
}
completionBlock(photoIsOnVacation);
}
}
}
答案 0 :(得分:4)
异步性趋于传播。一旦API异步,所有调用者都必须重新设计才能异步工作。因此,像- (BOOL) photoIsOnVacation
这样的方法是站不住脚的,因为它的接口是同步的 - 调用者希望在调用完成后立即得到答案 - 但是实现不会那样。
你必须重新设计- (void) checkIfPhotoIsOnVacationAndDo:(void(^)(BOOL photoIsOnVacation))block
之类的东西。这需要来自调用者的一个块,并在知道时使用答案调用该块。
答案 1 :(得分:2)
这就是信号量的用途:
bool waitForBlockToExecute()
{
__block bool value = false;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// sleep for a bit
sleep(1);
value = true;
// notify that the block is finished
dispatch_semaphore_signal(semaphore);
});
// wait for the semaphore
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore); // clean up the semaphore
return value;
}
显然,dispatch_async
块将替换为您的回调块,但我假设您从上面的代码中获取了图片。