8

ファイルのコピーを実行し、コピーされたファイルの量に関連する進行状況インジケーターを表示しようとするときに、よくある問題があるように思われることが、よく検索された後に表示されます。この問題を解決するためにかなりの時間を費やした後、私は再び StackOverflow Gods のなすがままになっていることに気づきました :-)

コピー プロセスのステータスを表示するプログレス バーを取得しようとしています。コピー プロセスが完了したら、Cocoa メソッドを呼び出します。課題 - NSFileManager では必要なすべての機能が得られないため、File Manager Carbon 呼び出しを利用する必要があります。

Matt Long のサイトCocoa Is My Girlfriendのコードを利用することから始めました。コードは私にかなりの距離を与えました。ファイルコピーの進行状況を機能させることができました。バーが更新され、(Apple ドキュメント内でさらに検索を行うことで) ファイル コピー プロセスが終了したかどうかを確認する方法がわかりました...

if (stage == kFSOperationStageComplete)

ただ、今の私の跳躍よりも少し大きい最後のハードルがあります。オブジェクト参照をコールバックに渡す方法がわかりません。また、終了後にコールバックから Cocoa メソッドを呼び出す方法もわかりません。これは、私の Carbon -> Cocoa -> Carbon の理解の限界です。ブログのコメントの1つが言った

「静的ポインターを介して進行状況インジケーターにアクセスする代わりに、FSFileOperationClientContext 構造体の void *info フィールドを使用して、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. 完了したら、通常の Cocoa メソッドにコールバックします

そして、いつものように、助けは大歓迎です (そして、うまくいけば、答えが私が多くのスレッドで見た問題や苦情の多くを解決するでしょう!!)

4

1 に答える 1

7

FSCopyObjectAsync()これは、型の構造体であるto の最後のパラメータを使用して行うことができますFSFileOperationClientContext。その構造体のフィールドの 1 つは です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];
}
于 2010-09-24T17:24:32.010 に答える