Try using @autoreleasepool to give the autoreleased memory a chance to drain:
for (int i = 0; i < [imgs count]; i++) {
dispatch_async(imageQueue1, ^{
@autoreleasepool {
NSString *path = [[NSString alloc] initWithFormat:@"%@/%@", damPath, [imgs objectAtIndex:i]];
NSURL *url = [[NSURL alloc] initWithString:path];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
NSString *savePath = [[NSString alloc] initWithFormat:@"%@/%@", newDir, [imgs objectAtIndex:i]];
if (![data writeToFile:savePath atomically:YES]) {
NSLog(@"Saving of \"%@\" failed!", [imgs objectAtIndex:i]);
}
NSString *progress = [[NSString alloc] initWithFormat:@"%d / %u downloaded...", i, [imgs count]];
NSLog(@"%@", progress);
}
});
}
Looking at the documentation, dispatch queues do provide autorelease pools, but there is no guarantee that they'll be drained, so for very memory intensive operations like this, you're probably well served by adding your own.
if your block creates more than a few Objective-C objects, you might want to enclose parts of your block’s code in an @autorelease block to handle the memory management for those objects. Although GCD dispatch queues have their own autorelease pools, they make no guarantees as to when those pools are drained. If your application is memory constrained, creating your own autorelease pool allows you to free up the memory for autoreleased objects at more regular intervals