FscopyObjectasyncを使用してファイルコピーの進行状況を表示します
-
05-10-2019 - |
質問
多くの検索の後、ファイルコピーを実行し、コピーされたファイルの量に対する進行状況インジケーターを表示しようとする場合、一般的な問題があるように思われます。この問題を解決しようとしてかなりの時間を費やした後、私は再びStackoverflowの神々に翻弄されていることに気づきます:-) - うまくいけば、私はルーキーを助けることができる人々の中にいるでしょう!
コピープロセスのステータスを表示するために進行状況バーを取得しようとしています。コピープロセスが終了したら、ココアメソッドを呼び出します。課題 - nsfilemanagerは私に必要な完全な能力を与えていないため、ファイルマネージャーのカーボンコールを使用する必要があります。
私はMatt Longのサイトでコードを利用しようとすることから始めました ココアは私のガールフレンドです. 。コードは私にある程度の距離を手に入れました。ファイルコピーの進捗状況を機能させることができました。バーの更新と(Appleドキュメント内で追加の検索を行う)ファイルコピープロセスが終了したかどうかを判断する方法を見つけました...
if (stage == kFSOperationStageComplete)
しかし、私は今私の飛躍よりも少し大きい最後のハードルを持っています。オブジェクト参照をコールバックに渡す方法がわかりません。終了したら、コールバックからココアメソッドを呼び出す方法がわかりません。これは私の炭素の限界です - > cocoa->炭素理解。ブログのコメントの1つは言った
「静的ポインターを介して進行状況インジケーターにアクセスする代わりに、FSFileOperationClientContext structのvoid *情報フィールドを使用し、AppDelegateまたはProgressインジケーター自体を渡すことができます。」
素晴らしいアイデアのように聞こえます。これを行う方法がわかりません。この問題にぶつかり、主にマットの例のコードに基づいて、炭素以外の背景から来ている他のすべての人のために、問題の例としていくつかの単純化されたコードがあります...
通常のココア法:
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...
}
}
したがって、一番下の行はどうすればよいですか:
- ポインターを通話方法からコールバックまでの進行状況バーのインスタンスに渡す
- 完了したら、通常のココアメソッドに電話をかけ直します
そしていつものように、助けが高く評価されています(そして、答えが私が多くのスレッドで見た多くの問題や苦情を解決することを願っています!!)
解決
最後のパラメーターを使用してこれを行うことができます FSCopyObjectAsync()
, 、タイプの構造です FSFileOperationClientContext
. 。その構造のフィールドの1つはです info
, 、これは基本的にFITのように使用できるボイド*パラメーターです。あなたが渡す構造体のそのフィールドに割り当てるものは何でも 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];
}