iPhone: Memory used by images is not freed - iphone

I have this code below that loads images from the web.
Those images are shown after clicking on a table cell, and they are reloaded every time the table cell is clicked.
The point is that analyzing the memory allocation with "Instruments", when I go back from the detail view to the table, the memory occupied from the images is not freed.
Does anyone have any suggestion?
I, of course, do all the release of the case... but it seems to don't help.
NSError *error = nil;
UIImage *img = nil;
NSString *myurl = [IMG_SERVER_URL stringByAppendingString: tmp_link.url];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSData *imageData = nil;
if(myurl!=nil){
imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:myurl] options:nil error:&error];
if (error == 0) {
img = [[UIImage alloc] initWithData:imageData];
}
}
[imageData release]; imageData = nil;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(targetWidth*i, 0, targetWidth, targetHeight)];
imageView.image = img;
imageView.contentMode = UIViewContentModeScaleAspectFit;
[img release];
[....some code....]
[imageView release];

I think "imageView.image = img" increases the reference count on the image object. If I'm right the allocated memory will not get freed as long as you don't release the imageView.

Related

Strange UIImageview remote image loading behaviour

I am trying to load remote image on my imageview using the following code. displayImage is referenced with imageview at testview.xib
- (void)viewDidLoad
{
[super viewDidLoad];
self.displayImage.userInteractionEnabled = YES;
NSMutableURLRequest *requestWithBodyParams = [NSMutableURLRequest requestWithURL:self.gImg.URL];
NSData *imageData = [NSURLConnection sendSynchronousRequest:requestWithBodyParams returningResponse:nil error:nil];
self.originalImage = [UIImage imageWithData:imageData];
self.displayImage.contentMode = UIViewContentModeScaleAspectFit;
self.displayImage = [[UIImageView alloc] initWithImage:self.originalImage];
[self.displayImage setImage:self.originalImage];
NSLog(#"DisplayImage Object:%#",self.displayImage);
NSLog(#"height of Displayimage image:%.f",self.displayImage.image.size.height);
}
But the image is not showing. I am certain image did loaded on imageview. Here is output log:
DisplayImage Object:<UIImageView: 0x11033270; frame = (0 0; 720 632); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1104c620>>
height of Displayimage image:632
Here I am modified your code:
No need to alloc the UIImageView in code, if you have the UIImageView in .xib.
NSURL *gImg = [NSURL URLWithString:#"http://www.vinfotech.com/sites/default/files/iphoe-app-design-ios7-way-12.jpg"];
NSMutableURLRequest *requestWithBodyParams = [NSMutableURLRequest requestWithURL:gImg];
NSData *imageData = [NSURLConnection sendSynchronousRequest:requestWithBodyParams returningResponse:nil error:nil];
UIImage *originalImage = [UIImage imageWithData:imageData];
self.displayImage.contentMode = UIViewContentModeScaleAspectFit;
// self.displayImage = [[UIImageView alloc] initWithImage:originalImage];
[self.displayImage setImage:originalImage];
NSLog(#"DisplayImage Object:%#",self.displayImage);
NSLog(#"height of Displayimage image:%.f",self.displayImage.image.size.height);
Hope so, this code helps you.
Create a dispatch queue:
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mayapp", NULL);
Use NSData method dataWithContentsOfURL and download the image in a background thread:
dispatch_async(backgroundQueue, ^(void) {
NSData *data = [NSData dataWithContentsOfURL:requestWithBodyParams];
//as soon as the image is downloaded, draw image in a main thread
dispatch_sync(dispatch_get_main_queue(), ^(void) {
self.originalImage = [UIImage imageWithData:data];
self.displayImage.contentMode = UIViewContentModeScaleAspectFit;
self.displayImage = [[UIImageView alloc] initWithImage:self.originalImage];
[self.displayImage setImage:self.originalImage];
});//close main block
});//background block
This Sample code works fine for me :
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:#"http://iceclearmedia.com/wp-content/uploads/2011/04/SEO-and-url-Shorteners.jpg"];
NSData* imageData = [[NSData alloc] initWithContentsOfURL:url];
UIImage* image = [[UIImage alloc] initWithData:imageData] ;
[self performSelectorOnMainThread:#selector(displayImage:) withObject:image waitUntilDone:NO];
}
- (void)displayImage:(UIImage *)image {
[self.imagedownload setImage:image]; //UIImageView
}

NSCache does not work

I'm writing an app that needs store a few images in cache. I'm trying to do it with NSCache and the code seems to be well but don't save the images in cache. I have this code:
cache is global, declared in .h: NSCache *cache;
-(UIImage *)buscarEnCache:(UsersController *)auxiliarStruct{
UIImage *image;
[[cache alloc] init];
NSLog(#"cache: %i", [cache countLimit]);
if ([cache countLimit] > 0) { //if [cache countLimit]>0, it means that cache isn't empty and this is executed
if ([cache objectForKey:auxiliarStruct.thumb]){
image = [cache objectForKey:auxiliarStruct.thumb];
}else{ //IF isnt't cached, is saved
NSString *imageURLString = [NSString stringWithFormat:#"http://mydomain.com/%#",auxiliarStruct.thumb];
NSURL *imageURL = [NSURL URLWithString:imageURLString];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
image = [UIImage imageWithData:imageData];
[cache setObject:image forKey:auxiliarStruct.thumb];
}
}else{ //This if is executed when cache is empty. IS ALWAYS EXECUTED BECAUSE FIRST IF DOESN'T WORKS CORRECTLY
NSString *imageURLString = [NSString stringWithFormat:#"http://mydomain.com/%#",auxiliarStruct.thumb];
NSURL *imageURL = [NSURL URLWithString:imageURLString];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
image = [UIImage imageWithData:imageData];
[cache setObject:image forKey:auxiliarStruct.thumb];
}
return image;
}
This function is called in other function with this:
UIImage *image = [self buscarEnCache:auxiliarStruct];
This works because the image is displayed on screen but isn't saved in cache, the line that I think fails is:
[cache setObject:image forKey:auxiliarStruct.thumb]; //auxiliarStruct.thumb is the name of the image
Someone knows why cache doesn't work?? Thanks!!
ps: sorry for my english, I know is bad
Every time the method buscarEnCache: is called an new cache object is created with the line:
[[cache alloc] init];
Thus the old cache just leaked and is not available any more.
place the cache = [[NSCache alloc] init]; in the init method of the class.
There is no need to check for the countLimit.
-(UIImage *)buscarEnCache:(UsersController *)auxiliarStruct{
UIImage *image = [cache objectForKey:auxiliarStruct.thumb];
if (!image) {
NSString *imageURLString = [NSString stringWithFormat:#"http://mydomain.com/%#",auxiliarStruct.thumb];
NSURL *imageURL = [NSURL URLWithString:imageURLString];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
image = [UIImage imageWithData:imageData];
[cache setObject:image forKey:auxiliarStruct.thumb];
}
return image;
}
You might want to place the fetch of the image in a method that runs in an other thread and return some kind of placeholder image.
As well as the answer provided by #rckoenes, you aren't allocating the cache instance correctly anyway; it should be:
cache = [[NSCache alloc] init];
Which should be moved into your init method.

upload image from url in scroll view

I have array of image in scroll view. I want to upload bigger image from url when i tap on image.
for example i tap on image"page-001.jpg" then check image data and then upload bigger image on image view and get back option so that go back to previous image view.
try this
NSMutableArray *arr = [[NSArray alloc] initWithObjects:imageURL,imageView.tag, nil];
[self performSelectorInBackground:#selector(loadImageInBackground:) withObject:arr];
- (void) loadImageInBackground:(NSArray *)urlAndTagReference
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Retrieve the remote image. Retrieve the imgURL from the passed in array
NSURL *imgUrl=[[NSURL alloc] initWithString:[urlAndTagReference objectAtIndex:0]];
NSData *imgData = [NSData dataWithContentsOfURL:imgUrl];
UIImage *img = [UIImage imageWithData:imgData];
[imgUrl release];
// Create an array with the URL and imageView tag to
// reference the correct imageView in background thread.
NSMutableArray *arr = [[NSMutableArray alloc ] initWithObjects:img,[urlAndTagReference objectAtIndex:1], nil ];
// Image retrieved, call main thread method to update image, passing it the downloaded UIImage
[self performSelectorOnMainThread:#selector(assignImageToImageView:) withObject:arr waitUntilDone:YES];
[arr release];
[pool release];
}
- (void) assignImageToImageView:(NSMutableArray *)imgAndTagReference
{
UIImageView *profilePic = (UIImageView *)[cell.contentView viewWithTag:20];
imageView.image = [imgAndTagReference objectAtIndex:0];
}
You can use SDWebImage. Just add the files to your project and use
[UIImageview setImageWithURL:(NSURL*)url];
This library also manage cache, and works very well in an UITableViewCell.

Saving a thumbnail on a background thread

I'm trying to create thumbnails (288x288) of selected photos from iPad photo library. I have an array of ALAsset objects presented in a UITableView and as I select a row, a larger preview (288x288) of that image is displayed. In order to prevent main thread blocking, I'm trying to create the thumbnail on a background thread and also cache a copy of the thumbnail to the file system.
In a view controller when a tableview row is selected, I call loadPreviewImage in background:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// get the upload object from an array that contains a ALAsset object
upload = [uploads objectAtIndex:[indexPath row]];
[self performSelectorInBackground:#selector(loadPreviewImage:)
withObject:upload];
}
I pass a custom upload object that contains asseturl property:
- (void)loadPreviewImage:(MyUploadClass*)upload
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *preview = [upload previewImage];
[self performSelectorOnMainThread:#selector(setPreviewImage:)
withObject:preview
waitUntilDone:YES];
[pool release];
}
This is called on main thread to display the thumbnail after it's loaded:
- (void)setPreviewImage:(UIImage*)image
{
self.imageViewPreview.image = image;
[self layoutSubviews];
}
This is a method of MyUploadClass:
- (UIImage *)previewImage
{
__block UIImage *previewImage = [[UIImage imageWithContentsOfFile:
[self uploadPreviewFilePath]] retain];
if (previewImage == nil && asseturl)
{
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library assetForURL:self.asseturl resultBlock:^(ALAsset *asset)
{
ALAssetRepresentation *rep = [asset defaultRepresentation];
previewImage = [UIImage imageWithCGImage: [rep fullScreenImage]];
previewImage = [[previewImage resizedImageWithContentMode:UIViewContentModeScaleAspectFit
bounds:CGSizeMake(288, 288)
interpolationQuality:kCGInterpolationHigh] retain];
NSData *previewData = UIImageJPEGRepresentation(previewImage, 1.0);
[previewData writeToFile:[self uploadPreviewFilePath] atomically:YES];
}
failureBlock:^(NSError *error){ }];
[library release];
}
return [previewImage autorelease];
}
The problem is that I always get nil previewImage the first time and only after the thumbnail is cached I get an image object. What am I doing wrong? Is there a better approach to this problem?
I didn't clearly understand how the resultBlock of ALAssetsLibrary operates, my mistake was to think that the execution is linear. It turns out that in my case the resultBlock executes on the main thread while the rest of the code in previewImage executes on a background thread. I was getting nil because previewImage returned before resultBlock had a chance to end its execution. I solved the problem by replacing previewImage with the following method:
- (void) loadPreviewImage:(CGSize)size withTarget:(id)target andCallback:(SEL)callback
{
NSString *path = [self uploadPreviewFilePath];
UIImage *previewImage = [UIImage imageWithContentsOfFile:path];
if (previewImage == nil && asseturl)
{
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library assetForURL:self.asseturl resultBlock:^(ALAsset *asset)
{
if (asset) {
ALAssetRepresentation *rep = [asset defaultRepresentation];
UIImage *img = [UIImage imageWithCGImage: [rep fullScreenImage]];
img = [img resizedImageWithContentMode:UIViewContentModeScaleAspectFit
bounds:size interpolationQuality:kCGInterpolationHigh];
NSData *previewData = UIImageJPEGRepresentation(img, 1.0);
[previewData writeToFile:path atomically:YES];
[target performSelectorOnMainThread:callback
withObject:img
waitUntilDone:YES];
}
}
failureBlock:^(NSError *error){ }];
[library release];
}
else {
[target performSelectorOnMainThread:callback withObject:img waitUntilDone:YES];
}
}

Xcode iPhone Programming: Loading a jpg into a UIImageView from URL

My app has to load an image from a http server and displaying it into an UIImageView
How can i do that??
I tried this:
NSString *temp = [NSString alloc];
[temp stringwithString:#"http://192.168.1.2x0/pic/LC.jpg"]
temp=[(NSString *)CFURLCreateStringByAddingPercentEscapes(
nil,
(CFStringRef)temp,
NULL,
NULL,
kCFStringEncodingUTF8)
autorelease];
NSData *dato = [NSData alloc];
dato=[NSData dataWithContentsOfURL:[NSURL URLWithString:temp]];
pic = [pic initWithImage:[UIImage imageWithData:dato]];
This code is in viewdidload of the view but nothing is displayed!
The server is working because i can load xml files from it. but i can't display that image!
I need to load the image programmatically because it has to change depending on the parameter passed!
Thank you in advance.
Antonio
It should be:
NSURL * imageURL = [NSURL URLWithString:#"http://192.168.1.2x0/pic/LC.jpg"];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * image = [UIImage imageWithData:imageData];
Once you have the image variable, you can throw it into a UIImageView via it's image property, ie:
myImageView.image = image;
//OR
[myImageView setImage:image];
If you need to escape special characters in your original string, you can do:
NSString * urlString = [#"http://192.168.1.2x0/pic/LC.jpg" stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSURL * imageURL = [NSURL URLWithString:urlString];
....
If you're creating your UIImageView programmatically, you do:
UIImageView * myImageView = [[UIImageView alloc] initWithImage:image];
[someOtherView addSubview:myImageView];
[myImageView release];
And because loading image from URL takes ages, it would be better to load it asynchronously. The following guide made it stupid-simple for me:
http://www.switchonthecode.com/tutorials/loading-images-asynchronously-on-iphone-using-nsinvocationoperation
Try this
NSURL *url = [NSURL URLWithString:#"http://192.168.1.2x0/pic/LC.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImageView *subview = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f,320.0f, 460.0f)];
[subview setImage:[UIImage imageWithData:data]];
[cell addSubview:subview];
[subview release];
All the Best.
This is the actual code.
UIImageView *myview=[[UIImageView alloc]init];
myview.frame = CGRectMake(0, 0, 320, 480);
NSURL *imgURL=[[NSURL alloc]initWithString:#"http://soccerlens.com/files/2011/03/chelsea-1112-home.png"];
NSData *imgdata=[[NSData alloc]initWithContentsOfURL:imgURL];
UIImage *image=[[UIImage alloc]initWithData:imgdata];
myview.image=image;
[self.view addSubview:myview];
You should load the image in the background or the view will freeze.
Try this:
UIImage *img = [[UIImage alloc] init];
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSData * data = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:#"http://www.test.com/test.png"];
img = [UIImage imageWithData: data];
dispatch_async(dispatch_get_main_queue(), ^{
//PUT THE img INTO THE UIImageView (imgView for example)
imgView.image = img;
});
});