7

После многократного поиска появляется много проблем при попытке сделать копию файла и отобразить индикатор прогресса относительно объема файла, который был скопирован. Проведя некоторое время, пытаясь решить эту проблему, я снова нахожусь во власти богов StackOverflow :-) - Надеюсь, однажды я буду среди тех, кто может помочь новичкам!Отображение прогресса копирования файла с использованием FSCopyObjectAsync

Я пытаюсь получить индикатор выполнения, чтобы показать статус процесса копирования, и как только процесс копирования завершится, вызовите метод Cocoa. Задача - мне нужно использовать вызовы File Manager Carbon, потому что NSFileManager не дает мне полной возможности, в которой я нуждаюсь.

Я начал с попытки использовать код на сайте Мэтта Лонга Cocoa Is My Girlfriend. Код дал мне хорошее расстояние. Мне удалось добиться успеха в копировании файлов. Обновления бара и (с некоторым дополнительным поиском в Apple, документы), которые я нашел, как сказать, если процесс копирования файлов закончило ...

if (stage == kFSOperationStageComplete)

Однако, у меня есть один последний барьер, который немного больше, чем мой прыжок прямо сейчас. Я не знаю, как передать ссылку на объект в обратный вызов, и я не знаю, как вызвать метод Какао из обратного вызова после его завершения. Это предел моего понимания Carbon -> Cocoa -> Carbon. Один из комментариев на блоге сказал

«Вместо того, чтобы получить доступ к индикатору прогресса через статический указатель, вы можете просто использовать пустое * информационное поле от структуры FSFileOperationClientContext и переходя либо в AppDelegate или сам индикатор прогресса «.

Звучит отличная идея. Не знаете, как это сделать. Ради всех остальных, которые, похоже, сталкиваются с этой проблемой и исходят из некарбонового фона, основанного главным образом на коде из примера Мэтта, вот пример упрощенного кода в качестве примера проблемы ...

в обычном способе какао:

CFRunLoopRef runLoop = CFRunLoopGetCurrent(); 
FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault); 

OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp, 
        runLoop, kCFRunLoopDefaultMode); 

if (status) { 
    NSLog(@"Failed to schedule operation with run loop: %@", status); 
    return NO; 
} 

// Create a filesystem ref structure for the source and destination and 
// populate them with their respective paths from our NSTextFields. 

FSRef source; 
FSRef destination; 

// Used FSPathMakeRefWithOptions instead of FSPathMakeRef which is in the 
// original example because I needed to use the kFSPathMakeRefDefaultOptions 
// to deal with file paths to remote folders via a /Volume reference 

FSPathMakeRefWithOptions((const UInt8 *)[aSource fileSystemRepresentation], 
    kFSPathMakeRefDefaultOptions, 
    &source, 
    NULL); 

Boolean isDir = true; 

FSPathMakeRefWithOptions((const UInt8 *)[aDestDir fileSystemRepresentation], 
    kFSPathMakeRefDefaultOptions, 
    &destination, 
    &isDir); 

// Needed to change from the original to use CFStringRef so I could convert 
// from an NSString (aDestFile) to a CFStringRef (targetFilename) 

CFStringRef targetFilename = (CFStringRef)aDestFile; 

// Start the async copy. 

status = FSCopyObjectAsync (fileOp, 
      &source, 
      &destination, // Full path to destination dir 
      targetFilename, 
      kFSFileOperationDefaultOptions, 
      statusCallback, 
      1.0, 
      NULL); 

CFRelease(fileOp); 

if (status) { 

    NSString * errMsg = [NSString stringWithFormat:@"%@ - %@", 
          [self class], status]; 

     NSLog(@"Failed to begin asynchronous object copy: %@", status); 
} 

Тогда обратного вызова (в том же файле)

static void statusCallback (FSFileOperationRef fileOp, 
      const FSRef *currentItem, 
      FSFileOperationStage stage, 
      OSStatus error, 
      CFDictionaryRef statusDictionary, 
      void *info) 
{ 

    NSLog(@"Callback got called."); 

    // If the status dictionary is valid, we can grab the current values to 
    // display status changes, or in our case to update the progress indicator. 

    if (statusDictionary) 
    { 

     CFNumberRef bytesCompleted; 

     bytesCompleted = (CFNumberRef) CFDictionaryGetValue(statusDictionary, 
       kFSOperationBytesCompleteKey); 

     CGFloat floatBytesCompleted; 
     CFNumberGetValue (bytesCompleted, kCFNumberMaxType, 
           &floatBytesCompleted); 

     NSLog(@"Copied %d bytes so far.", 
           (unsigned long long)floatBytesCompleted); 

     // fileProgressIndicator is currently declared as a pointer to a 
     // static progress bar - but this needs to change so that it is a 
     // pointer passed in via the controller. Would like to have a 
     // pointer to an instance of a progress bar 

     [fileProgressIndicator setDoubleValue:(double)floatBytesCompleted]; 
     [fileProgressIndicator displayIfNeeded]; 
    } 

if (stage == kFSOperationStageComplete) { 

    NSLog(@"Finished copying the file"); 

    // Would like to call a Cocoa Method here... 
} 

} 

Таким образом, нижняя линия как я могу:

  1. передать указатель на экземпляр индикатора выполнения от способа вызова функции обратного вызова
  2. После завершения обратного вызова к нормальному способу какао

И как всегда, помощь очень ценится (и, надеюсь, ответ решает многие проблемы и жалобы, которые я видел во многих потоках !!)

ответ

7

Вы можете сделать это, используя последний параметр: FSCopyObjectAsync(), который является структурой типа FSFileOperationClientContext. Одним из полей этой структуры является info, который является параметром void *, который вы можете использовать по своему усмотрению. Независимо от того, что вы назначаете этому полю структуры, которую вы передаете в FSCopyObjectAsync(), в свою очередь будет передана ваша функция обратного вызова в качестве последнего параметра функции info. Пустота * может быть любой, включая указатель на объект, поэтому вы можете использовать это, чтобы передать экземпляр вашего объекта, с которым вы хотите обработать обратный вызов.

код установки будет выглядеть следующим образом:

FSFileOperationClientContext clientContext = {0}; //zero out the struct to begin with 

clientContext.info = myProgressIndicator; 
//All the other setup code 
status = FSCopyObjectAsync (fileOp, 
     &source, 
     &destination, // Full path to destination dir 
     targetFilename, 
     kFSFileOperationDefaultOptions, 
     statusCallback, 
     1.0, 
     &clientContext); 

Затем в функции обратного вызова:

static void statusCallback (FSFileOperationRef fileOp, 
     const FSRef *currentItem, 
     FSFileOperationStage stage, 
     OSStatus error, 
     CFDictionaryRef statusDictionary, 
     void *info) 
{ 
    NSProgressIndicator* fileProgressIndicator = (NSProgressIndicator*)info; 
    [fileProgressIndicator setDoubleValue:(double)floatBytesCompleted]; 
    [fileProgressIndicator displayIfNeeded]; 
} 
+1

Брайан спасибо !!! Теперь я вижу это, решение кажется таким простым! Я закончил инициализацию экземпляра класса, чтобы полностью обработать панель прогресса и передал этот экземпляр класса в clientContext. Мне удалось обновить индикатор выполнения и вызвать вызовы методов для класса. Я отправлю полное решение для тех, кто не совсем следил за моими последними комментариями, когда я получаю мгновение. Еще раз спасибо Брайан! – Hooligancat

+0

@Hooligancat Можете ли вы разместить свое полное решение? У меня та же проблема: я хотел бы передать экземпляр класса в clientContext, но я не знаю, как это сделать. – FR6

+0

@ FR6 - Я не коснулся этого кода через некоторое время, но я возьму копание и посмотрю, смогу ли я найти его для вас и разместить весь код. Оставайтесь с нами ... – Hooligancat