Question

Il apparaît après beaucoup de recherches qu'il semble y avoir un problème commun en essayant de faire une copie de fichier et affiche un indicateur de progression par rapport à la quantité du fichier qui a été copié. Après avoir passé un temps considérable à essayer de résoudre ce problème, je me retrouve à la merci des Dieux StackOverflow encore une fois :-) - Je espère qu'un jour je serai parmi ceux qui peuvent aider les rookies trop

Je tente d'obtenir une barre de progression pour afficher l'état d'un processus de copie et une fois le processus de copie est terminée, appeler une méthode Cocoa. Le défi - je dois utiliser les appels File Manager Carbon car NSFileManager ne me donne pas la pleine capacité je besoin

.

J'ai commencé en essayant d'utiliser le code sur le site de Matt Long Le cacao est mon amie . Le code m'a fait une bonne distance. J'ai réussi à obtenir la copie de fichiers de travail de progression. Les mises à jour de bar et (avec quelques recherches supplémentaires dans les documents d'Apple) j'ai découvert comment savoir si le processus de copie de fichier est terminé ...

if (stage == kFSOperationStageComplete)

Cependant, j'ai un dernier obstacle qui est un peu plus grand que mon saut en ce moment. Je ne sais pas comment passer une référence d'objet dans le rappel et je ne sais pas comment appeler une méthode Cocoa à partir de la fonction de rappel une fois terminé. Ceci est une limite de mon Carbon -> Cocoa -> compréhension carbone. L'un des commentaires sur le blog dit

  

« Au lieu d'accéder à l'indicateur de progression via un pointeur statique, vous pouvez simplement utiliser le champ info vide * de la struct FSFileOperationClientContext, et en passant soit le AppDelegate ou l'indicateur de progression lui-même. »

On dirait une excellente idée. Je ne sais pas comment faire. Par souci de tout le monde qui semble se cognent sur cette question et vient d'un milieu non-carbone, basée principalement sur le code de l'exemple de Matt, voici un code simplifié comme un exemple du problème ...

Dans un procédé de cacao normal:

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

Ensuite, le rappel (dans le même fichier)

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

} 

Ainsi, la ligne de fond est de savoir comment puis-je:

  1. Passez un pointeur vers une instance d'une barre de progression de la méthode d'appel à la fonction de rappel
  2. Une fois terminée, retour d'appel vers une méthode normale de cacao

Et comme toujours, l'aide est très appréciée (et je l'espère la réponse résoudra bon nombre des problèmes et des plaintes que j'ai vu dans beaucoup de fils !!)

Était-ce utile?

La solution

Vous pouvez le faire en utilisant le dernier paramètre à FSCopyObjectAsync(), qui est une struct de type FSFileOperationClientContext. L'un des champs de cette struct est info, qui est un paramètre vide de * que vous pouvez utiliser essentiellement comme bon vous semble. Tout ce que vous attribuez à ce champ de la struct vous passez en FSCopyObjectAsync() sera transmis à son tour à votre fonction de rappel que le dernier paramètre de la fonction info il. Un vide * peut être quelque chose, y compris un pointeur vers un objet, de sorte que vous pouvez l'utiliser pour passer l'instance de votre objet que vous souhaitez gérer le rappel.

Le code de configuration ressemblera à ceci:

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);

Ensuite, dans votre fonction de rappel:

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];
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top