在后台线程上不调用FSCopyObjectAsync回调方法

时间:2012-04-24 20:55:26

标签: macos asynchronous copy macos-carbon core-foundation

我正在使用FSCopyObjectAsync跨卷复制文件。我已经使用cimgf中的代码让我继续前进并且它运行良好。

我遇到的最后一个问题是后台线程上没有发生复制状态回调。当我没有通过dispatch_async(copyQueue, ^{开始复制操作时,回调被完美地调用。当我将它移动到后台时,它不会发射。这是代码:

//Excerpt from existing method
// Create the semaphore, specifying the initial pool size
fd_sema = dispatch_semaphore_create(1);

dispatch_queue_t copyQueue = dispatch_queue_create("copy.theQueue", 0);
dispatch_group_t group = dispatch_group_create();

for(SearchPath * p in searchPaths) {
    dispatch_async(copyQueue, ^{

        NSString * newDestination = [NSString stringWithFormat:@"%@%@",destination,p.relativePath];
        NSString * source = [NSString stringWithFormat:@"%@%@",p.basePath,p.relativePath];
        NSError * error = nil;

        //Wait until semaphore is available  
        dispatch_semaphore_wait(fd_sema, DISPATCH_TIME_FOREVER);

        //Update progress window text
        [progressview.label setStringValue:[NSString stringWithFormat:@"Copying \"%@\" to \"%@\"",[source lastPathComponent],[destination lastPathComponent]]];

        if(p.isDirectory) {

            BOOL result = [[NSFileManager defaultManager] createDirectoryAtPath:newDestination withIntermediateDirectories:NO attributes:nil error:nil];

            if(result) {
                //Item was a directory
                dispatch_semaphore_signal(fd_sema);
            }

        }else{

            [self startCopy:source dest:[newDestination stringByDeletingLastPathComponent]];

        }

        if(error) {
            MTLogDebug(@"Error! : %ld", error.code);
        }
    }); //End async
} //End for loop

//End excerpt  

- (void)startCopy:(NSString *)source dest:(NSString *) destination
{
    // Get the current run loop and schedule our callback
    //TODO:Make this work while on a background thread
    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;
    }

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

    //FSPathMakeRef( (const UInt8 *)[source fileSystemRepresentation], &sourceRef, NULL );
    FSPathMakeRefWithOptions((const UInt8 *)[source fileSystemRepresentation],
                             kFSPathMakeRefDefaultOptions, 
                             &sourceRef, 
                             NULL);

    Boolean isDir = true;
    //FSPathMakeRef( (const UInt8 *)[destination fileSystemRepresentation], &destinationRef, &isDir );    
    FSPathMakeRefWithOptions((const UInt8 *)[destination fileSystemRepresentation],
                             kFSPathMakeRefDefaultOptions, 
                             &destinationRef, 
                             &isDir);

    // Start the async copy.
    status = FSCopyObjectAsync (fileOp,
                                &sourceRef,
                                &destinationRef, // Full path to destination dir
                                NULL, // Use the same filename as source
                                kFSFileOperationDefaultOptions,
                                statusCallback,
                                0.1,
                                NULL);
    NSLog(@"Stat: %d",status);
    CFRelease(fileOp);

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

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

        NSNumber *bytesCompleted = (__bridge NSNumber *) CFDictionaryGetValue(statusDictionary, kFSOperationBytesCompleteKey);

        NSURL *url = (__bridge NSURL *)convertedURLRef;

        if([bytesCompleted intValue] > 0) {

            if(stage == kFSOperationStageRunning) {

                //Update progress indicator
                [progressview.indicator setDoubleValue:progressview.indicator.doubleValue + [newNumberValue floatValue]];           
            }
        }
    }

    if (stage == kFSOperationStageComplete) {
        dispatch_semaphore_signal(fd_sema);
    }
}

感谢任何帮助或见解!

1 个答案:

答案 0 :(得分:2)

问题是你在dispatch_async使用它,但是FSCopyObjectAsync将复制操作回调绑定到特定的runloop,从而绑定了一个特定的线程。

您需要做的是使用dispatch_async,但要么:

  1. 在主线程上执行复制操作(应该可以正常,就像在主线程上执行NSURLConnection一样正常)
  2. 分离辅助NSThread,在该线程上安排操作,然后通过调用[[NSRunLoop currentRunLoop] run](或相应的变体)启动runloop运行。