Frage

Im pretty new to Cocoa development, and I probably do not clearly understand how ARC works.

My problem is that when I'm using NSImageView it is not getting deallocated as I want so the program is leaking memory.

__block CMTime lastTime = CMTimeMake(-1, 1);
__block int count = 0;
[_imageGenerator generateCGImagesAsynchronouslyForTimes:stops
                                      completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime,
                                                                AVAssetImageGeneratorResult result, NSError *error)
     {
         if (result == AVAssetImageGeneratorSucceeded)
         {
             if (CMTimeCompare(actualTime, lastTime) != 0)
             {
                 NSLog(@"new frame found");
                 lastTime = actualTime;
             }
             else
             {
                 NSLog(@"skipping");
                 return;
             }

             // place the image onto the view
             NSRect rect = CGRectMake((count+0.5) * 110, 100, 100, 100);

             // the problem is here!!! ImageView object gets allocated, but never released by the program even though I'm using ARC 

             NSImageView *imgV = [[NSImageView alloc] initWithFrame:rect];

             [imgV setImageScaling:NSScaleToFit];
             NSImage *myImage = [[NSImage alloc] initWithCGImage:image size:(NSSize){50.0,50.0}];
             [imgV setImage:myImage];
             [self.window.contentView addSubview: imgV];
         }

         if (result == AVAssetImageGeneratorFailed)
         {
             NSLog(@"Failed with error: %@", [error localizedDescription]);
         }
         if (result == AVAssetImageGeneratorCancelled)
         {
             NSLog(@"Canceled");
         }
         count++;
     }];

Therefore, when I'm returning to this block again t generate new images and display them, everything works perfect except that my program memory use increases by the number of views got created.

If anyone can help me with this I would really appreciate it! Thank you!

War es hilfreich?

Lösung

Your problem is that you don't remove your subviews when you are generating new ones - make sure you remove your subviews before with something along those lines:

NSArray *viewsToRemove = [self.contentView subviews];
for (NSView *v in viewsToRemove) {
    [v removeFromSuperview];
}

So your problem is not related to the usage of ARC actually. Each time you create a NSImageView and add it to contentView it is your responsability to remove them before adding a series of new ones. Note that adding those views to contentView will increment the ref count by one and removing them from the contentView will decrement the ref count by one leading to the memory usage for those views being freed by the system (because nothing else is retaining your views in btw).

Andere Tipps

Offending piece of code:

[self.window.contentView addSubview: imgV];

You've allocated an NSImageView. and keep adding it to the view. You never remove it, meaning the view is creating many references to different instances of the same object, all allocating their own piece of memory.

Solution: You'll need to keep track of the view, to make sure you can remove it later. Typically, I use class extensions.

For example:

@interface ClassName() {
    NSImageView* m_imgV;
}
@end

....

// place the image onto the view
NSRect rect = CGRectMake((count+0.5) * 110, 100, 100, 100);

if (m_imgV) {
    [m_imgV removeFromSuperView];
}
m_imgV = [[NSImageView alloc] initWithFrame:rect];

[m_imgV setImageScaling:NSScaleToFit];
NSImage *myImage = [[NSImage alloc] initWithCGImage:image size:(NSSize){50.0,50.0}];
[m_imgV setImage:myImage];
[self.window.contentView addSubview:m_imgV];

I was fighting with this problem for the whole day and finally found the way. For some reason the program wanted me to add a whole function which looks like:

// remove all the view from the superview
// and clean up a garbage array
-(void) killAllViews
{
   for (NSImageView *iv in _viewsToRemove)
   {
      [iv removeFromSuperview];
   }
   [_viewsToRemove removeAllObjects]; // clean out the array
}

where _viewsToRemove is an array of NSImageViews which I'm filling every time my block is generating new images and adds them to the view.

Still don't understand why just adding the pure code from inside my killAllViews method somewhere into program couldn't solve the problem. Right now I'm basically doing the same, but just calling this method.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top