UIImage released, but CGImage not released

It seems I have a memory management issue when I create instances of UIImage that CGImages are not released.

I have a UIScrollView page to scroll through a series of JPG images. Below is my entire class, which is a page view in the paging scroll view.

The code runs in the main thread. The code uses ARC. I tried loading images using imageWithContentsOfFile: (returns an object with auto-implementation), as well as initWithContentsOfFile: (returns a saved object). I tried @autoreleasepool and used performSelectorOnMainThread to ensure that the code runs in the main thread, as suggested in other posts.

When scrolling images, the memory memory grows only until the application is completed, as shown in the screenshot from the tools. Note the high io image highlight.

Screenshot using virtual memory

Virtual memory tracker

The following screenshot shows that the GTGImageScrollerPageViews, UIImageViews, and UIImages commands are being deleted. Note that there are Transitory objects for this numbering in the high 300s. However, CGImages are not exempted, and the number of living CGImages is at high 400 and 0 Transitory.

Display Screenshot

Allocations

EDIT: I used to recycle and reuse instances of GTGImageScrollerPageView in ScrollView, as this is a common template for scrollviews like this. To simplify this while trying to debug this problem, I allow to release the entire GTGImageScrollerPageView after it is displayed in the ScrollView. As you can see in the second image above, there are only 4 live GTGImageScrollerPageView and 377 transient, there are also 388 UIImageViews and 389 UIIMages listed as transient, so it seems that UIImageViews and UIImages are freed up perfectly.

If I manually release CGImage with CGImageRelease (in the code below), CGImages are freed. I know that I should not do this because I do not own CGImage, but it is useful for checking that this is where the problem arises. The screenshots below show the same code tested in Tools, but with CGImageRelease without comment.

Screenshot showing virtual memory usage using CGImageRelease

Virtual memory usage

Screenshot showing distributions using CGImageRelease

enter image description here

In the profiled pins using CGImageRelease, you can see that the correct number of CGImage objects is Living and Transitory, and this memory does not grow indefinitely. In addition, the application does not crash during use using CGImageRelease.

If it was some kind of CGImage system caching, then it should free up memory when raising a warning about memory, but that is not the case. The memory continues to grow.

What could be the reason for this unlimited growth in memory?

Here is the code to view the page

EDIT: In response to the comments, I updated the code to simplify it, thereby eliminating distractions like ivars and properties that are not needed to demonstrate the problem. The problem remains the same, and when profiling, the results are the same. I also added to NSLog, which also outputs a stream. I see that dealloc is called as expected in GTGImageScrollerPageView, and it is always thread # 1, and the call to displayAspectThumbImage is always in thread # 1.

I really do not believe that something is wrong with the code that is presented here, and this is confirmed by Rob's generous efforts. Something else causes this, but all the code related to loading and displaying the image is here; there is no other code here. The only great thing I could think of raising is that the displayAspectThumbImage method is called from scrollViewDidScroll and scrollViewDidEndDecellerating the scrollview delegate methods, but the fact that the methods are called in the main thread should eliminate the problems of autocompletion due to starting in another thread.

I checked and NSZombies is not included, and there are no zombies that increase the use of my memory. Indeed, when the CGImageRelease method is called, the memory usage is pretty flat, as seen in the screenshot above.

@implementation GTGImageScrollerPageView - (void)dealloc { NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSThread currentThread]); } - (void)displayAspectThumbImage:(NSString *)path { NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSThread currentThread]); UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds]; [self addSubview:imageView]; UIImage *image = [[UIImage alloc] initWithContentsOfFile:path]; [imageView setImage:image]; //If uncommented CGImages are disposed correctly //CGImageRelease([imageView.image CGImage]); } @end 

Tested:

iOS 6.1.4

iOS 5.0.1

+6
source share
3 answers

It is really a pity that I myself answer this question, but thought it would be useful to share my experience.

It turned out to be a category in UIImage, which was part of a third-party library that I used. I will not name the library, as I tested with the latest version, and the problem is not that there is nothing to win by naming the library.

This is especially strange since the library was not used somewhere close to the code that was leaking. It was used only in one place throughout the project, but at any time when I used UIImage, it seems that this affected the instance of UIImage. The mere presence of this category in the project was enough. This was a real surprise.

The way I decided this was to start by modeling the script in a new project and found that the code wasn’t leaking there. Then I changed the code a bit, and when I moved the categories through the leak, appeared.

Many thanks to Rob for his generous efforts to help me with this, and even though he did not provide a solution directly, communicating with him was really useful for solving the problem. It's nice to know that there are such cool people.

+4
source

I made a simple endless scroller using your code, and after scrolling through almost 100 images, the memory usage was, as one would expect, completely invisible:

allocations

vm

Looking at your source, I would recommend a few little things (for example, I would add aspectImageView to the private class extension, not to .h, I assume that you are installing pageIndex from the calling routine, but I would add it as the displayAspectThumbImage parameter, I would made the aspectImageView property a weak and edited the code accordingly (for example, create an image representation, add it as a subview, and then set the weak imageview property to point to this object), etc.), but none of them have a direct relationship to your problem here.

On the bottom line, your problem is not stored in the code that you shared with us.

+3
source

Try using a property instead of ivar.

@property (non-atomic, strong) UIImageView * aspectImageView;

0
source

Source: https://habr.com/ru/post/946047/


All Articles