2012-06-10 39 views
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内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); 
     } 
    } 
} 

回答

4

异步性往往会扩散,一旦你使API异步,所有的调用者都必须重新设计,以便异步工作,因此像- (BOOL) photoIsOnVacation这样的方法是站不住脚的,因为它的接口是同步的 - 调用者期望有一个答案一旦通话完成 - 但实施不会那样工作。

你必须重新设计,像- (void) checkIfPhotoIsOnVacationAndDo:(void(^)(BOOL photoIsOnVacation))block。这会从调用者处获取一个块,并在知道该块时调用该块。

+0

谢谢,肯,我欣赏指导。我当然注意到了转移异步性的倾向。现在就实施你的战略。 –

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块将与你的回调块替换,但我相信你从上面的代码的图片。

+0

问题是许多异步API依赖于当前线程返回到运行循环。所以,通过这些API,您正在使用这种方法引发僵局。有关示例,请参阅http://stackoverflow.com/a/10868897/1312143以及提问者如何尝试并发现它发生死锁。 –