0

I try to make thumbs on my iPad app of all the view in the background using the following code:

NSString *path = [self.page previewPathForOrientation:currentOrientation];
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
  @autoreleasepool {
    UIGraphicsBeginImageContextWithOptions(self.previewView.bounds.size, NO, 0.0);
    [self.previewView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    self.previewView = nil;
    float scale = [UIScreen mainScreen].scale;
    CGRect previewRect = currentOrientation == Landscape ? [[OrientationLandscape singleton] frameForPreviewImage] : [[OrientationPortrait singleton] frameForPreviewImage];
    CGSize previewSize = CGSizeMake(previewRect.size.width * scale, previewRect.size.height * scale);
    UIImage *scaledImage = [image scaleImageToSize:previewSize];

    CGImageDestinationRef imageDestination = CGImageDestinationCreateWithURL((__bridge CFURLRef)[[NSURL alloc] initFileURLWithPath:path], (__bridge CFStringRef)@"public.png", 1, NULL);
    CGImageDestinationAddImage(imageDestination, [scaledImage CGImage], NULL);
    CGImageDestinationFinalize(imageDestination);
    CFRelease(imageDestination);

    NSFileManager *fileMngr = [[NSFileManager alloc] init];
    if(![fileMngr fileExistsAtPath:path])
    {
      ZAssert(0, @"could not save preview file");
    }
    dispatch_async(dispatch_get_main_queue(), ^{
      rendered++;
      //DLog(@"rendered %d items", rendered);
      [GetController addSkipBackupAttributeToItemAtPath:path];
      [self.page setPreviewRenderedForOrientation:currentOrientation];
      contentsCount = 0;
      currentContentIndex = 0;
      //[self prepareOtherOrientation];
      if(self.journal == nil && (![self.page previewRenderedForOrientation:Landscape] || ![self.page previewRenderedForOrientation:Portrait])){
        [self appendPage:self.page];
      }
      DLog(@"rendered page %@ in orientation %d", self.page, currentOrientation);
      self.page = nil;
      [self retry];
    });
  }
});

The retry function uses an NSTimer to start the same function again, after a short delay and with a different page. Using the Allocations tool, the heap just keeps growing. After a while I get Memory Warnings, shortly after the app crashes.

Everything works fine when I remove all the dispatch calls, but of course thats not what I want. Also, when I increase the delay in the retry method to say 5 seconds, the problem disappears too, so it seems memory isn't released when things get processed in quick succession.

I absolutely ensured that this method isn't running more than once at a time... any ideas what's going on here?

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
cboe
  • 469
  • 1
  • 9
  • 25
  • 1
    You don't need to create an autoreleasepool inside a dispatch queue BTW - the only case where you may is if you are allocating a bunch of objects in a loop. – Richard J. Ross III Nov 04 '12 at 18:39
  • I agree with Richard. Also an autorelease pool may be deferring release calls till later. It sounds like somewhere in there you're getting into recursively calling blocks. Every time a block is created it block-copies the local stack, which could include blocks that are copying other blocks that are copying other blocks... etc etc. – Jack Lawrence Nov 04 '12 at 19:02
  • yeah the autoreleasepool was a tryout, didn't change anything. I think Jack is right, but I'm not sure if I get it, do you have an example? the retry function calls this function again, is that the problem? how could I avoid that? – cboe Nov 04 '12 at 19:07
  • Try this: `[self performSelectorOnMainThread:@selector(retry) withObject:self waitUntilDone:NO];`. With this code I think that: 1- you will allocate the timer always on main thread. It could be a problem if you create a timer on a thread and invalidate in another (but I don't think is your case, just to be sure without seeing code). 2- the wait until done = NO should let the block "finalize" itself (it goes to the next instruction, the end of the block) and start another retry from the main thread without cycles. Try and let me know – LombaX Nov 04 '12 at 21:50
  • sadly that didn't do it, same problem... even without the call, since i'm using dispatch_async(dispatch_get_main_queue()...) beforehand shouldnt that let the block "finalize" and execute everything on the main thread as well? – cboe Nov 04 '12 at 23:42
  • I think you can check with NSLog. Put an nslog line after the retry call and check if it is executed correctly. – LombaX Nov 05 '12 at 13:28
  • everythings executed correctly, but after a while i get memory warnings, and again what's curios, when i increase the delay between retrys to say 5 seconds it seems to work... i'm using a NSOperationQueue instead of dispatch now, and I've set concurrentOperations to 1, but it makes no difference. – cboe Nov 05 '12 at 17:53

0 Answers0