Вопрос

I am trying to write a program using Objective-C/XCode that backs up one directory (source dir) into another (dest dir).

When I test the program on a small directory on my local machine, it works as expected. But when I try a large directory, or anything over a network, the program beachballs. I know that threading is the answer. Given the following code one can tell I have been fiddling with various methods to do this. Can anyone help out? I can't seem to get this working properly. Here is the code/method in question:

- (void)doSync:(NSString *)sURL {
bStopCopy = NO;
NSString *sSource = [[pcSource URL] path];
NSString *sDestination = [[pcDestination URL] path];
NSString *sSourcePath = [sSource stringByAppendingString:@"/"];
NSString *sDestinationPath = [sDestination stringByAppendingString:@"/"];
NSString *sSourceFile;
NSString *sDestinationFile;
NSString* file;
NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtPath:sURL];

while ((file = [enumerator nextObject]) && (bStopCopy == NO)) {
    [btMainWindowStopQuitButton setTitle: @"Stop..."];
    [btMainWindowStopQuitButton setTag:1];
    bCopyInProgress = YES;
    __block NSError *eErrorMessage;
    sSourceFile = [sSourcePath stringByAppendingString:file];
    sDestinationFile = [sDestinationPath stringByAppendingString:file];
    // check if it's a directory & exists at destination
    BOOL isDirectory = NO;
    BOOL isFileExistingAtDestination = NO;
    __block BOOL isThereAnError = NO;
    [[NSFileManager defaultManager] fileExistsAtPath: [NSString stringWithFormat:@"%@/%@",sURL,file]
                                         isDirectory: &isDirectory];
    isFileExistingAtDestination = [[NSFileManager defaultManager] fileExistsAtPath: sDestinationFile];

    if (!isDirectory) {
        if (!isFileExistingAtDestination) {
            //                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
            //                    if (![[NSFileManager defaultManager] copyItemAtPath:sSourceFile toPath:sDestinationFile error: &eErrorMessage]) {
            //                        NSLog(@"File Copy Error: %@", eErrorMessage);
            //                        isThereAnError = YES;
            //                    }
            //                });

            //[oqFileCopy addOperationWithBlock:^{
            dispatch_queue_t copyQueue = dispatch_queue_create("Copy File", NULL);
            dispatch_async(copyQueue, ^{
                if (![[NSFileManager defaultManager] copyItemAtPath:sSourceFile toPath:sDestinationFile error: &eErrorMessage]) {
                    NSLog(@"File Copy Error: %@", eErrorMessage);
                    isThereAnError = YES;
                }
            //[oqMain addOperationWithBlock:^{
                dispatch_async(dispatch_get_main_queue(), ^{
                    llFileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath: sDestinationFile error: Nil] fileSize];
                    [[[tvDialogueLabel textStorage] mutableString] setString:
                     [NSString stringWithFormat:@"%@\nCopied to: %@ (%qu bytes)", [[tvDialogueLabel textStorage] string], sDestinationFile, llFileSize]];
                    NSRange endPoint = NSMakeRange ([[tvDialogueLabel string] length], 0);
                    [tvDialogueLabel scrollRangeToVisible: endPoint];
                    llTotalFileSize = llTotalFileSize + llFileSize;
                });
            });
            //                NSLog(@"%@", sSourceFile);
            //                NSLog(@"%@", sDestinationFile);
        } else if (isFileExistingAtDestination) {
            [[[tvDialogueLabel textStorage] mutableString] setString:
             [NSString stringWithFormat:@"%@\nFile: %@ | Already Synced.", [[tvDialogueLabel textStorage] string], sDestinationFile]];
            NSRange endPoint = NSMakeRange ([[tvDialogueLabel string] length], 0);
            [tvDialogueLabel scrollRangeToVisible: endPoint];
        }
    }
    else if (isDirectory) {
        if (!isFileExistingAtDestination) {
            if (![[NSFileManager defaultManager] createDirectoryAtPath:sDestinationFile withIntermediateDirectories:YES attributes:nil error: &eErrorMessage]){
                NSLog(@"Directory Create Failed: %@", eErrorMessage);
                isThereAnError = YES;
            }
            [[[tvDialogueLabel textStorage] mutableString] setString:
             [NSString stringWithFormat:@"%@\nCreated Directory: %@", [[tvDialogueLabel textStorage] string], sDestinationFile]];
            NSRange endPoint = NSMakeRange ([[tvDialogueLabel string] length], 0);
            [tvDialogueLabel scrollRangeToVisible: endPoint];
            //                NSLog(@"%@", sSourceFile);
            //                NSLog(@"%@", sDestinationFile);
        } else if (isFileExistingAtDestination) {
            [[[tvDialogueLabel textStorage] mutableString] setString:
             [NSString stringWithFormat:@"%@\nDirectory: %@ | Already Exists.", [[tvDialogueLabel textStorage] string], sDestinationFile]];
            NSRange endPoint = NSMakeRange ([[tvDialogueLabel string] length], 0);
            [tvDialogueLabel scrollRangeToVisible: endPoint];
        }
        [self doSync: file];
    }

    if (isThereAnError) {
        NSLog(@"There was an error!");
        //[_wDialogue setTitle: @"Error while syncing..."];
        break;
    }
    //        NSLog(@"%@", @"==================================================");
}

}

Это было полезно?

Решение

The easiest way to do this might be to remove all the block code from your method, and simply make your call to doSync: using performSelectorInBackground:withObject:. For example:

[foo performSelectorInBackground:@selector(doSync:) withObject:myURL];

Другие советы

Another easy way of doing this, if you're using OSX 10.6 and up, would be to throw all this code to Grand Central Dispatch. The while loop is what needs to be on a different thread, that's the one that's holding up the main thread. By wrapping the whole thing into a dispatch_async(), you're moving that while loop on a different thread as well.

- (void)doSync:(NSString *)sURL {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

        // your doSync code goes here
    });
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top