Problem with loading image thumbnails - iphone

I wrote some naive code(in the sense that it's synchronous calls) for a tableview that contains thumbnails of images pulled from a url. The code in cellForRowAtIndexPath that pulls the image goes like this:
data = (data == nil)? [[NSData alloc] initWithContentsOfURL:photoThumbPage] : [data initWithContentsOfURL:photoThumbPage];
thumbImg = (thumbImg == nil)? [[UIImage alloc] initWithData:data] : [thumbImg initWithData:data];
I felt that there might be a speedup with not allocating a new nsdata and uiimage each time, so I defined them in my class. Each time I get a thumbnail, I want to check if they are defined, and if they already are, I just initialize them again with different values.
As I am writing this question here, you can already guess that all sorts of bad things happenned:) When I scroll through my tableview, sometimes, two or more entries will share the same image, and I get some errors in console that do not crash my app, but tell me that my jpeg file is corrupted.
Putting aside the fact that each of these requests should be asynchronous, and that I really should be caching these results, can anyone tell me where I might be tripping up? It seems that if cellForRowAtIndexPath is being called for each cell that is visible, and my code is synchronous, there should be no reason why the nsdata and uiimage variables should persist over the calls(they are wiped out by the init methods). I swapped out these member variables and initialized them each time:
NSData *data_local = [[NSData alloc] initWithContentsOfURL:photoThumbPage];
UIImage *thumbImg_local = [[UIImage alloc] initWithData:data];
and it works just fine. Is there any merit in allocating a reusable member variable rather than allocating a new NSData and UIImage each time i want to load a thumbnail? Or is that just a recipe for disaster? Thanks for any comments/help.

The issue is not that you are reusing member variables but that you are reusing the same space in memory. When you call init on data the second time, you are still using the same memory space as before (the space that came from the alloc method). This results in more than one UIImage pointing to the same area of memory, hence the multiple images, and the underlying data changing from underneath each UIImage when a new one is created, hence the error messages about bad jpegs.

Related

iphone how to manage memory when frequently loading images

I'm currently coding my app that loads images frequently (but not the same images). As far as I test it out, there's no memory leak problem, but the memory usage of the app increases as I load up different images. It means the app will eventually gets killed by OS when it hits memory cap. I inspected through instrument and I found that a lot of memory was being held by NSConcreteData.
//from different thread, and pass data to main thread
NSData *data = [[[NSData alloc] initWithContentsOfURL:url] autorelease];
//at main, data to uiimage
imgView.image = [[[UIImage alloc] initWithData:data] autorelease];
When the view is dismissed and all allocated memories get return to heap, it seems like the memory
I allocate for NSData keep presents in the memory and therefore the memory usage of the app increases compared to what it was before it loaded images. I'm not sure if this is normal behavior.
Or is it bad practice to pass allocated memory between different threads?
This may or may not help in your exact case, but this is often reduced by paying closer attention to your autorelease stack growth. You should be able to reduce the problem by wrapping those heavy creators (data from url, image with data) in autorelease pool blocks.
#autoreleasepool { work with large NSObjects here }
or, depending on the system you must deploy for:
NSAutoreleasePool * pool = [NSAutoreleasePool new];
work with large NSObjects here
[pool release];
If you can use #autoreleasepool, do so. it's slightly better because it interfaces with the underlying autorelease stacks directly. If you need backwards compatibility, then use NSAutoreleasePool. At a higher level, they really serve the same purpose, in the sense that you should be able to interchange their implementations in your program without introducing new issues. Therefore, it really comes down to the minimum OS you are targeting and the build settings you have specified for your project when deciding which to use.
You should wrap your handling and creation of large (or many) allocations in autorelease blocks because autoreleased objects are sent release messages "at some point in the future". By creating and destroying that autorelease pool explicitly (and by using autorelease less often), you allow many of these objects to be destroyed much sooner.
As to why this can be good in addition to simply not using autorelease in your program: clients and system libraries may end up adding your big/numerous images/NSDatas to the autorelease pools. Autorelease pools are like (thread local) stacks -- when you destroy your local pool, all those autorelease messages made while your pool was on top will be fulfilled, and the autoreleased objects will receive their release messages.
Or is it bad practice to pass allocated memory between different threads?
Remember that you should message your UIKit and AppKit objects from the main thread. In Cocoa, many libraries may specify their threading models. NSData follows the Foundation threading model. The objects are not explicitly thread safe, but they are safe to use if you read and/or write from no more than one thread at any given time (that is, use a lock whenever you need to use it in a MT context, or pass copies). Passing and sharing data/objects is not a bad practice, it's necessary (or the logical solution) at times. There is a slight catch saying it's 'not bad': many people do not understand multithreading and shared resources well (it is not a trivial skill to learn for many).
Try something like this. Autorelease is less precise than explicitly releasing.
//on background thread data's retain count will now be 1
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
//on main thread (I'm assuming .image is a retained property)
UIImage *newImage = [[UIImage alloc] initWithData:data];
imgView.image = newImage;
[data release];
[newImage release];
try open Allocation to calculate memory,
and when every time you want to release something,
Make it to be nil first, make sure no one is using this variable.
Otherwise, it crash.
Make Variable equal to nil, before release.

Running out of memory with UIImage creation on an offscreen Bitmap Context by NSOperation

I have an app with multiple UIView subclasses that acts as pages for a UIScrollView. UIViews are moved back and forth to provide a seamless experience to the user. Since the content of the views is rather slow to draw, it's rendered on a single shared CGBitmapContext guarded by locks by NSOperation subclasses - executed one at once in an NSOperationQueue - wrapped up in an UIImage and then used by the main thread to update the content of the views.
-(void)main {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];
if([self isCancelled]) {
return;
}
if(nil == data) {
return;
}
// Buffer is the shared instance of a CG Bitmap Context wrapper class
// data is a dictionary
CGImageRef img = [buffer imageCreateWithData:data];
UIImage * image = [[UIImage alloc]initWithCGImage:img];
CGImageRelease(img);
if([self isCancelled]) {
[image release];
return;
}
NSDictionary * result = [[NSDictionary alloc]initWithObjectsAndKeys:image,#"image",id,#"id",nil];
// target is the instance of the UIView subclass that will use
// the image
[target performSelectorOnMainThread:#selector(updateContentWithData:) withObject:result waitUntilDone:NO];
[result release];
[image release];
[pool release];
}
The updateContentWithData: of the UIView subclass performed on the main thread is just as simple
-(void)updateContentWithData:(NSDictionary *)someData {
NSDictionary * data = [someData retain];
if([[data valueForKey:#"id"]isEqualToString:[self pendingRequestId]]) {
UIImage * image = [data valueForKey:#"image"];
[self setCurrentImage:image];
[self setNeedsDisplay];
}
// If the image has not been retained, it should be released together
// with the dictionary retaining it
[data release];
}
The drawLayer:inContext: method of the subclass will just get the CGImage from the UIImage and use it to update the backing layer or part of it. No retain or release is involved in the process.
The problem is that after a while I run out of memory. The number of the UIViews is static. CGImageRef and UIImage are created, retained and released correctly (or so it seems to me). Instruments does not show any leaks, just the free memory available dip constantly, rise a few times, and then dip even lower until the application is terminated. The app cycles through about 2-300 of the aforementioned pages before that, but I would expect to have the memory usage reach a more or less stable level of used memory after a bunch of pages have been already skimmed at fast speed or, since the images are up to 3MB in size, deplete way earlier.
Any suggestion will be greatly appreciated.
I realize this is an old posting, but in case it helps anybody else .... This looks like a case of memory fragmentation. We have an app that behaves the same way. The amount of memory actually allocated by the app never reaches dangerous levels, but if you look at the amount of resident memory for the app (using VM Tracker snapshots in the Allocations Instrument, or the Activity Monitor Instrument), it climbs inexorably over time until a not-very-large transient spike kills the app.
The app in question is a multi-threaded app that makes tons of transient allocations in a large range of sizes, the timing of which can't be predicted or controlled. Such an app has to be paranoid about releasing unneeded memory allocations, not because they take up too much memory per se, but because they can create holes that prevent larger images from fitting into the allocated blocks. Even smaller allocations that tend to be overlooked are important in fragmentation (granted that the low-level allocator does group allocations by size, which is helpful to an extent). Memory zones are theoretically helpful for addressing fragmentation but pretty hard to make effective, at least in my experience. Also, use custom auto-release pools, or better yet, alloc/init as much as you can, and release as early as possible. The fact that the underlying frameworks are always making their own allocations for caching purposes probably doesn't help.

Caching behavior of UIImage

Does UIImage ever removes images from its cache? Can I keep a pointer to an image I got from imageNamed: and use it as long as I like or must I always call imageNamed:?
The UIImage object that is returned from imageNamed: is treated like all other objects as far a memory management goes. If you want to keep the reference to the object between method calls, you should retain it and release it when you are done to decrement the reference count.
UIImage * cachedImage;
-(void) getTheImage {
UIImage * cachedImage = [[UImage imageNamed:#"MyImage.png"] retain];
//Do something with the image...
}
//In some other method or dealloc
[cachedImage release];
Also, note that the UIImage class reference says:
In low-memory situations, image data
may be purged from a UIImage object to
free up memory on the system. This
purging behavior affects only the
image data stored internally by the
UIImage object and not the object
itself. When you attempt to draw an
image whose data has been purged, the
image object automatically reloads the
data from its original file. This
extra load step, however, may incur a
small performance penalty.
UIImage caches the data itself. You must not hold a pointer and just pass that around. That can be unsafe since when there is a memory warning and there was no strong ref to that object then UIImage will purge cached data. Call [UIImage imageNamed:] every time. It is fast and returns the ref to the image from memory. If the image is no longer in memory it will reload it and pass that ref

objective-c memory management: caching view elements

I'm writing an application which is quite graphically heavy and therefore I'm trying to implement a caching mechanism within my view controller that creates a view once, and retains it for future use, similar to the following:
- (UIView *)logoView
{
if(_logoView == nil)
{
_logoView = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"logo_header.png"]];
_logoView.contentMode = UIViewContentModeCenter;
}
return _logoView;
}
This all works fantastically, however, I'm getting a bit confused over a certain issue; when I simulate a memory warning I call a method which purges the cache, like so:
- (void)purgeCachedContent
{
if(_logoView != nil)[_logoView release];
}
However, this seems to be causing my application issues, as it can get called several times (dealloc, didReceiveMemoryWarning and viewDidUnload).
How can I determine if an instance exists to send it a release message? Even when I try to NSLog a released view I receive a EXC_BAD_ACCESS error, so I'm having trouble figuring out the best way I'd do it. I even removed the if statement hoping to rely on being able to send nil objects messages, but this causes the same error...
I'm sure it's something simple missing from my objective-c knowledge... I just don't know what!
_logoView isn't set to nil automatically just by releasing it, so any future methods you try to call using that pointer will go to a memory location that used to contain a valid object, but now contains junk. You can fix this by setting _logoView = nil; after releasing it.
If you want to cache it why would you want to release it?
just use autorelease on init:
_logoView = [[[UIImageView alloc] initWithImage: [UIImage imageNamed: #"logo_header.png"]] autorelease];

Memory issue in iPhone?

I am using single UIImageView object, allocated memory it once. But changing the image names multiple times.
The question is that,
A) one UIImageView object,allocated once and never changed image name.
e.g UIImageView *firstObj = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"blue.png"]];
B) another UIImageView object,allocated once and changed image names multiple times
e.g UIImageView *secondObj = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"blue.png"]];
//changed image name once,
secondObj.image = [UIImage imageNamed:#"green.png"];
and so on....
which objects uses maximum memory or use equal memory?
which is the best way to use secondObj with minimum memory utilization?
please explain it briefly because i need to use number of image in my project and i want to avoid memory issue due to the images.
Reformatting your code, your second example is:
UIImageView *secondObj = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"blue.png"]];
// changed image name once
secondObj.image = [UIImage imageNamed:#"green.png"];
// and so on....
This code is fine. When you assign an image to an instance of UIImageView, it will increase the image's retain count by one. If an image is already assiged, it will release it first.
Since [UIImage imageNamed:...] will give you an object already marked for autorelease, you can continue to assign images as shown in the example with no memory leaks. Once the UIImageView releases the existing image, it will be collected by an Autorelease pool.
In terms of minimising memory usage, the [UIImage imageNamed:...] method does store the image in a small amount of application-wide cache memory that you don't exert any direct control over. The cache does have an upper limit limit, but you can't flush it to reclaim memory, so using it will increase your memory footprint as you fetch new UIImages.
You may want to consider avoiding this cache by using [UIImage imageWithData:...] to load your images, which is discussed in the Stackoverflow question [UIImage imageNamed…] vs [UIImage imageWithData…].
I'm no great Objective-C expert but your usage in B looks like it should work without leaking. Since no other objects (other than the UIImageView) are retaining the UIImages, they should be released when they are replaced in the UIImageView and the garbage collector should do its work.
David
A UIImage created using +imageNamed: is autoreleased so it will go away. To give an example:
UIImage *image1 = [UIImage imageNamed:#"blue.png"]; //image1 has retain count 1
[myImageView setImage:image1]; //image1 is retained by UIImageView so has retain count 2
//run loop ends, all auto released objects are released and image1 now has retain count 1
//Later in app
UIImage *image2 = [UIImage imageNamed:#"green.png"]; //image2 has retain count 1
[myImageView setImage:image2]; //image1 is released and now has retain count 0, so the memory is freed, image2 now has retain count 2 as it is retained
Since no other objects (other than the UIImageView) are retaining the UIImages, they should be released when they are replaced in the UIImageView and the garbage collector should do its work.
Keep in mind that there is no garbage collector on the iPhone.
You can find more about that here:
StackOverflow:Is garbage collection supported for iPhone applicaions?
(This should actually be a comment to Davids answer but I don't have enough reputation yet)