Reputation: 29448
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!
Upvotes: 1
Views: 1605
Reputation: 5539
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.
Upvotes: 1