简体   繁体   中英

For loops with dispatch_async / GCD crash

So I'm trying to understand GCD. I have a long running operation that just appends data after I download like this:

        NSFileManager *fileManager = [NSFileManager defaultManager];
        __block NSFileHandle *output;
        output = [NSFileHandle fileHandleForUpdatingAtPath:tempPath];
        __block NSError *error = nil;
        BOOL success;
        dispatch_queue_t stitchQueue = dispatch_queue_create("com.test", NULL);

for (NSString *packetName in listOfFiles) {
           dispatch_async(stitchQueue, {
                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                NSString *packetPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:packetName];

            NSData *packetData = [[NSData alloc] initWithContentsOfFile:packetPath];

            [output seekToEndOfFile];
            [output writeData:packetData];

            [fileManager removeItemAtPath:packetPath error:&error];
            NSLog(@"Removed file after appending data success: %i Error: %@", success, [error localizedDescription]);
            [self updateManifestColumn:@"IsParsed" withValue:YES forFile:packetName inTable:tableName];

            packetData = nil;
            [packetData release];
            [pool release];
            });
        }
        [output closeFile];

//dispatch_async( // do my next long run task after the data is stitched together)

This code works if I remove the dispatch_async calls. Am I doing something wrong? When I run it with the dispatch_async, it gets through one iteration successfully then crashes. It crashes on bad access on the NSFileHandle. It seems to be dealloc'd after 1 iteration. I'm not sure what I need to do to fix this. Thanks!

Crash is caused by __block specifier. Normally, every object variable from surrounding scope that block is using within is retained for lifetime of this block. This is especially useful for dispatch_async or dispatch_after , and means that object will be valid as long as block is not completed. This is true power of blocks that is not visible at first sight. However, when you want to alter object pointer or primitive variable value, you need to use __block specifier. This specifier treats object variables differently and don't retain them by block, thus allowing them to be deallocated before block completes. This is exactly what's happening to you. In line:

output = [NSFileHandle fileHandleForUpdatingAtPath:tempPath];

you're creating autoreleased object which will be released in near future. Since you're using dispatch_async this probably happens when block is still running, thus creating crashes.

There're two solutions:

  • You can make your local variable an instance variable of class, to extend lifetime of this object,

  • But the easiest way would be to remove __block because you don't change object's pointer inside your block and you don't need this specifier at all.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM