Отображение хода копирования файла с помощью FSCopyObjectAsync - PullRequest
7 голосов
/ 24 сентября 2010

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

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

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

if (stage == kFSOperationStageComplete)

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

"Вместо того, чтобы обращаться к индикатору прогресса через статический указатель, вы можете просто использовать информационное поле void * структуры 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. После завершения отзовитесь к обычному методу Какао

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

1 Ответ

7 голосов
/ 24 сентября 2010

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

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

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];
}
...