0

我有以下使用块和完成处理程序的方法:

    // 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(dispatch_get_main_queue(),^{ 中嵌入 photoOnFile 分配,但这没有帮助。任何指导表示赞赏。

更新:这是成功结合 Ken 推荐的解决方案的重新编写的代码:

    - (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);
        }
    }
}
4

2 回答 2

4

异步性趋于传播。一旦你使一个 API 异步,它的所有调用者也必须重新设计为异步工作。因此,像您这样的方法- (BOOL) photoIsOnVacation是站不住脚的,因为它的接口是同步的——调用者希望在调用完成后立即得到答案——但实现方式并非如此。

您必须重新设计为类似- (void) checkIfPhotoIsOnVacationAndDo:(void(^)(BOOL photoIsOnVacation))block. 这从调用者那里获取一个块,并在已知时调用带有答案的块。

于 2012-06-10T19:55:16.353 回答
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块将替换为您的回调块,但我假设您从上面的代码中获得了图片。

于 2012-06-10T20:06:24.697 回答