Memory management issue with AssetsLibrary framework - iphone

My code take a lot time to load images from gallery. I store url of each image in database and then load them from gallery using AssetsLibrary framework. But i notice that it take more memory and so that i receive memory warning. after some time app is crash down. Here i put my code.
-(void) nextImageLoad
{
// ALAssetsLibrary* self. assetslibrary=[[ALAssetsLibrary alloc] init];
self.myScrollView.pagingEnabled=YES;
NSLog(#"current :- %d, loaded_photo :- %d, [imageData count] :- %d",current, loaded_photo, [imageData count]);
if(current>loaded_photo && current < [imageData count])
{
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
self.rep = [myasset defaultRepresentation];
CGImageRef iref = [self.rep fullResolutionImage];
if (iref)
{
UIImageView *temp = [[UIImageView alloc] initWithFrame:CGRectMake(self.x, y, 300, 300)];
temp.image=[UIImage imageWithCGImage:iref];
[self.myScrollView addSubview:temp];
[temp release];
[myScrollView setContentSize:CGSizeMake(self.x+600, 410)];
[myScrollView setAutoresizesSubviews:YES];
}
else
{
}
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"booya, cant get image - %#",[myerror localizedDescription]);
};
[self.assetslibrary assetForURL:[NSURL URLWithString:[[self.imageData objectAtIndex:current] valueForKey:#"photo_url"] ]
resultBlock:resultblock
failureBlock:failureblock];
self.x=self.x+320;
loaded_photo= loaded_photo+1;
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
lastContentOffset = scrollView.contentOffset.x;
}
-(void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
if (lastContentOffset > scrollView.contentOffset.x)
{
NSLog(#"left");
if(current>0)
current=current-1;
}
else if (lastContentOffset < scrollView.contentOffset.x)
{
NSLog(#"right");
current=current+1;
[self nextImageLoad];
}
lastContentOffset = scrollView.contentOffset.x;
}

Hard to know if this is the problem, but try creating an autorelease pool inside all your blocks:
block = ^(ALAsset *myasset)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// rest of block here
[pool release];
}
Also remember if you return early inside the block, you need to release the pool object before your return statement.

Related

why the images orientation is changing when loading the image from the cache?

I have an application in which i am loading the images from the image caches.`
if(![[ImageCache sharedImageCache] hasImageWithKey:imagepath])
{ cell.bannerview2.image = [UIImage imageNamed:#"no_imags.png"];
NSArray *myArray = [NSArray arrayWithObjects:cell.bannerview2,imagepath,#"no_imags.png",[NSNumber numberWithBool:NO],nil];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate performSelectorInBackground:#selector(updateImageViewInBackground:) withObject:myArray];
}
else
{
cell.bannerview2.image = [[ImageCache sharedImageCache] getImagefromCacheOrUrl:imagepath];
}
}
else
{
cell.bannerview2.image = [UIImage imageNamed:#"no_imags.png"];
}
this is how i am doing the image cache process.But the problem is when the image is larger than the required frame the image orientation is changing.this is my image updating code in the appdelegate
-(void)updateImageViewInBackground:(NSArray *)idsArray{
//NSLog(#"IN METHOD idsArray %#",idsArray);
NSString *iconurl = (NSString *)[idsArray objectAtIndex:1];
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
if([[ImageCache sharedImageCache] hasImageWithKey:iconurl]){
[self performSelectorOnMainThread:#selector(updateImageViewInForeground:) withObject:idsArray waitUntilDone:NO];
[pool release];
return;
}
if([iconurl length] > 0){
[[ImageCache sharedImageCache] saveImageToCacheOfUrl:iconurl];
}
[self performSelectorOnMainThread:#selector(updateImageViewInForeground:) withObject:idsArray waitUntilDone:NO];
[pool release];
}
-(void)updateImageViewInForeground:(NSArray *)idsArray{
UIImageView *weatherView = (UIImageView *)[idsArray objectAtIndex:0];
NSString *iconurl = (NSString *)[idsArray objectAtIndex:1];
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
weatherView.image = [[ImageCache sharedImageCache] getImagefromCacheOrUrl:iconurl];
if(weatherView.image == nil){
NSString *defaultname = (NSString *)[idsArray objectAtIndex:2];
weatherView.image = [UIImage imageNamed: defaultname];
}
BOOL resizeImgView = [(NSNumber *)[idsArray objectAtIndex:3] boolValue];
if(resizeImgView){
//NSLog(#"resizeImgView %# %#",iconurl,weatherView);
weatherView.frame = CGRectMake(weatherView.frame.origin.x, weatherView.frame.origin.y, weatherView.image.size.width, weatherView.image.size.height);
}
[pool release];
}
can anybody help me?

ALAsset Photo Library Image Performance Improvements When Its Loading Slow

Hi i've got a problem with display an image on my scrollView.
At first i create new UIImageView with asset url:
-(void) findLargeImage:(NSNumber*) arrayIndex
{
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
ALAssetRepresentation *rep;
if([myasset defaultRepresentation] == nil) {
return;
} else {
rep = [myasset defaultRepresentation];
}
CGImageRef iref = [rep fullResolutionImage];
itemToAdd = [[UIImageView alloc] initWithFrame:CGRectMake([arrayIndex intValue]*320, 0, 320, 320)];
itemToAdd.image = [UIImage imageWithCGImage:iref];
[self.scrollView addSubview:itemToAdd];
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"Cant get image - %#",[myerror localizedDescription]);
};
NSURL *asseturl = [NSURL URLWithString:[self.photoPath objectAtIndex:[arrayIndex intValue] ]];
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:asseturl
resultBlock:resultblock
failureBlock:failureblock];
}
Where itemToAdd is a UIImageView define in interface:
__block UIImageView *itemToAdd;
And scrollView define as a property:
#property (nonatomic, strong) __block UIScrollView *scrollView;
Then in my viewWillAppear i do this:
- (void) viewWillAppear:(BOOL)animated {
self.scrollView.delegate = self;
[self findLargeImage:self.actualPhotoIndex];
[self.view addSubview:self.scrollView];
}
But image doesnt appear, should i refresh self.view after add image to scrollView, or should do something else?
ALAssetsLibrary block will execute in separate thread. So I suggest to do the UI related stuffs in main thread.
To do this either use dispatch_sync(dispatch_get_main_queue() or performSelectorOnMainThread
Some Important Notes:
Use AlAsset aspectRatioThumbnail instead of fullResolutionImage for high performance
Example:
CGImageRef iref = [myasset aspectRatioThumbnail];
itemToAdd.image = [UIImage imageWithCGImage:iref];
Example:
-(void) findLargeImage:(NSNumber*) arrayIndex
{
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
CGImageRef iref = [myasset aspectRatioThumbnail];
dispatch_sync(dispatch_get_main_queue(), ^{
itemToAdd = [[UIImageView alloc] initWithFrame:CGRectMake([arrayIndex intValue]*320, 0, 320, 320)];
itemToAdd.image = [UIImage imageWithCGImage:iref];
[self.scrollView addSubview:itemToAdd];
});//end block
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"Cant get image - %#",[myerror localizedDescription]);
};
NSURL *asseturl = [NSURL URLWithString:[self.photoPath objectAtIndex:[arrayIndex intValue] ]];
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:asseturl
resultBlock:resultblock
failureBlock:failureblock];
}
Also change the order of viewWillAppear()
- (void) viewWillAppear:(BOOL)animated {
self.scrollView.delegate = self;
[self.view addSubview:self.scrollView];
[self findLargeImage:self.actualPhotoIndex];
}
You are manipulating the view from another thread.
You must use the main thread for manipulating the view.
Add image to scrollView using:
dispatch_async(dispatch_get_main_queue(), ^{
[self.scrollView addSubview:itemToAdd];
}
or using:
[self.scrollView performSelectorOnMainThread:#selector(addSubview:) withObject:itemToAdd waitUntilDone:NO];
Please refer:
NSObject Class Reference
GCD

Determine selected photos in AGImagePickerController

I'm using AGImagePickerController. Im having a hard time figuring out how to import the images selected into my iCarousel which is in another carousel. I know that the success block in it contains the selected image. I can't seem to import it in my awakeFromNib or putting it in an array.
here is my code that calls the AGImagePickerController:
-(IBAction) cameraRoll {AGImagePickerController *imagePickerController = [[AGImagePickerController alloc] initWithFailureBlock:^(NSError *error) {
if (error == nil)
{
NSLog(#"User has cancelled.");
[self dismissModalViewControllerAnimated:YES];
} else
{
NSLog(#"Error: %#", error);
// Wait for the view controller to show first and hide it after that
double delayInSeconds = 0.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self dismissModalViewControllerAnimated:YES];
});
}
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
} andSuccessBlock:^(NSArray *info) {
NSLog(#"Info: %#", info);
[self dismissModalViewControllerAnimated:YES];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
}];
[self presentModalViewController:imagePickerController animated:YES];
[imagePickerController release];
}
In my awakeFromNib:
- (void)awakeFromNib
{
if (self) {
self.images = [NSMutableArray arrayWithObjects:#"111.jpg",
#"112.jpg",
#"113.jpg",
#"114.jpg",
#"115.jpg",
#"116.jpg",
#"117.jpg",
#"118.png",
#"119.jpg",
#"120.jpg",
nil];
}
}
Then I implement this for my carousel :
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index
{
//create a numbered view
UIView *view = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[images objectAtIndex:index]]];
return view;
}
You should be able to look at the contents of the info array using your NSLog statement. It would have been helpful to see the contents of that log statement.
I found this here and it might work:
for (i = 0; i < info.count; i++) {
ALAssetRepresentation *rep = [[info objectAtIndex: i] defaultRepresentation];
UIImage * image = [UIImage imageWithCGImage:[rep fullResolutionImage]];
}
Edit:
You need to save the images (either in memory or by writing them to files) after the user selects them. If you want to write them to files, you can do that like so:
[UIImageJPEGRepresentation(image, 1.0) writeToFile:jpgPath atomically:YES];
If the AGImagePickerController code is in your iCarousel class, you can just add the images to your images array. Your successBlock would look something like this:
self.images = [NSMutableArray new];
for (i = 0; i < info.count; i++) {
ALAssetRepresentation *rep = [[info objectAtIndex: i] defaultRepresentation];
UIImage * image = [UIImage imageWithCGImage:[rep fullResolutionImage]];
[self.images addObject:image];
}
And finally, in your iCarousel code you need to read the images either from your images array or from the disk (depending on where you chose to save them). If you are saving them in memory
your code could look like this:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index
{
return [[UIImageView alloc] initWithImage:[self.images objectAtIndex:index]];
}
Or, if you decided to save the images to disk then you could recreate the UIImage objects using [UIImage imageWithContentsOfFile:filePath];.

MFMailComposeViewController: Attaching Images from Photo Gallery

I am having a bit of trouble attaching images from the Photo Gallery to an email.
Basically, one of the features of my application allow the user to take photos. When they snap the shot, I record the URL Reference to the image in Core Data. I understand that you have to go through the ALAssetRepresentation to get to the image. I have this up and running within my application for when the user wants to review an image that they have taken.
I am now attempting to allow the user to attach all of the photos taken for an event to an email. While doing this, I iterate through the Core Data entity that stores the URL References, call a method that returns a UIImage from the ALAssetsLibrary and then attaches it using the NSData/UIImageJPEGRepresentation and MFMailComposeViewController/addAttachmentData methods.
Problem is: When the email is presented to the user, there are small blue squares representing the images and the image is not attached.
Here is the code:
- (void)sendReportReport
{
if ([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer.mailComposeDelegate = self;
[mailer setSubject:#"Log: Report"];
NSArray *toRecipients = [NSArray arrayWithObjects:#"someone#someco.com", nil];
[mailer setToRecipients:toRecipients];
NSError *error;
NSFetchRequest *fetchPhotos = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Photo" inManagedObjectContext:__managedObjectContext];
[fetchPhotos setEntity:entity];
NSArray *fetchedPhotos = [__managedObjectContext executeFetchRequest:fetchPhotos error:&error];
int counter;
for (NSManagedObject *managedObject in fetchedPhotos ) {
Photo *photo = (Photo *)managedObject;
// UIImage *myImage = [UIImage imageNamed:[NSString stringWithFormat:#"%#.png", counter++]];
NSData *imageData = UIImageJPEGRepresentation([self getImage:photo.referenceURL], 0.5);
// NSData *imageData = UIImagePNGRepresentation([self getImage:photo.referenceURL]);
// [mailer addAttachmentData:imageData mimeType:#"image/jpeg" fileName:[NSString stringWithFormat:#"%i", counter]];
[mailer addAttachmentData:imageData mimeType:#"image/jpeg" fileName:[NSString stringWithFormat:#"a.jpg"]];
counter++;
}
NSString *emailBody = [self getEmailBody];
[mailer setMessageBody:emailBody isHTML:NO];
[self presentModalViewController:mailer animated:YES];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Failure"
message:#"Your device doesn't support the composer sheet"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
and the method that returns the UIImage:
#pragma mark - Get Photo from Asset Library
+ (ALAssetsLibrary *)defaultAssetsLibrary {
static dispatch_once_t pred = 0;
static ALAssetsLibrary *library = nil;
dispatch_once(&pred, ^{
library = [[ALAssetsLibrary alloc] init];
});
return library;
}
- (UIImage *)getImage:(NSString *)URLReference
{
__block UIImage *xPhoto = nil;
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
UIImage *xImage;
// get the image
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullScreenImage];
if (iref) {
xImage = [UIImage imageWithCGImage:iref];
}
xPhoto = xImage;
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"Error fetching photo: %#",[myerror localizedDescription]);
};
NSURL *asseturl = [NSURL URLWithString:URLReference];
// create library and set callbacks
ALAssetsLibrary *al = [DetailsViewController defaultAssetsLibrary];
[al assetForURL:asseturl
resultBlock:resultblock
failureBlock:failureblock];
return xPhoto;
}
NOTE: This above code does run, it simply does not attach the image. Also, note, I am able to successfully attach images from the gallery within my application, as long at I have set them into a UIImageView.Image already (basically, I am taking the pointer to the image from the UIImageView and passing it to the addAttachmentData method.) It is just when I attempt to iterate through Core Data and attach without first setting the image into a UIImageView that I have the trouble.
Any tips would be greatly appreciated!
Thanks!
Jason
Oh now I see it.. sry. You are using an asynchronous block to get the image from the asset library. But right after starting that operation, you are returning xImage. But the asynchronous operation will finish later. So you are returning nil.
You need to change your architectur to smth like this:
In your .h file you need two new members:
NSMutableArray* mArrayForImages;
NSInteger mUnfinishedRequests;
In your .m file do smth like that:
- (void)sendReportReport
{
// save image count
mUnfinishedRequests = [fetchedPhotos count];
// get fetchedPhotos
[...]
// first step: load images
for (NSManagedObject *managedObject in fetchedPhotos )
{
[self loadImage:photo.referenceURL];
}
}
Change your getImage method to loadImage:
- (void)loadImage:(NSString *)URLReference
{
NSURL *asseturl = [NSURL URLWithString:URLReference];
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
// get the image
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullScreenImage];
if (iref) {
[mArrayForImages addObject: [UIImage imageWithCGImage:iref]];
} else {
// handle error
}
[self performSelectorOnMainThread: #selector(imageRequestFinished)];
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"Error fetching photo: %#",[myerror localizedDescription]);
[self performSelectorOnMainThread: #selector(imageRequestFinished)];
};
// create library and set callbacks
ALAssetsLibrary *al = [DetailsViewController defaultAssetsLibrary];
[al assetForURL:asseturl
resultBlock:resultblock
failureBlock:failureblock];
}
Create a new callback method:
- (void) imageRequestFinished
{
mUnfinishedRequests--;
if(mUnfinishedRequests <= 0)
{
[self sendMail];
}
}
And an extra method for finally sending the mail, after getting the images:
- (void) sendMail
{
// crate mailcomposer etc
[...]
// attach images
for (UIImage *photo in mArrayForImages )
{
NSData *imageData = UIImageJPEGRepresentation(photo, 0.5);
[mailer addAttachmentData:imageData mimeType:#"image/jpeg" fileName:[NSString stringWithFormat:#"a.jpg"]];
}
// send mail
[...]
}

Background thread running for fetching image from Facebook album

- (void)request:(FBRequest *)request didLoad:(id)result {
// NSArray *resultData = [result objectForKey:#"data"];
NSMutableArray *photos = [[NSMutableArray alloc] initWithCapacity:1];
// imageArray = [[NSMutableArray alloc] initWithCapacity:1];
NSArray *resultData = [result objectForKey:#"data"];
if ([resultData count] > 0)
{
noOfImages = 0;
for (NSUInteger i=0; i<[resultData count] ; i++)
{
[photos addObject:[resultData objectAtIndex:i]];
NSDictionary *albumPhotos = [photos objectAtIndex:i];
NSString *imageUrl = [albumPhotos objectForKey:#"picture"];
NSLog(#"ImageURL:%#",imageUrl);
NSURL *url = [NSURL URLWithString:imageUrl];
// [slideImageArray addObject:url];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *images = [UIImage imageWithData:data];
// imageView.image = images;
[slideImageArray addObject:images];
[NSThread detachNewThreadSelector:#selector(startTheBackgroundJob:) toTarget:self withObject:images];
}
NSLog(#"ImageCount:%d",[slideImageArray count]);
if([slideImageArray count] == 1)
{
imageView.image = [slideImageArray objectAtIndex:0];
}
if([slideImageArray count]>1)
{
numTimer =0;
myTimer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(timerRunning) userInfo:nil repeats:YES];
}
}
}
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
NSLog(#"Err message: %#", [[error userInfo] objectForKey:#"error_msg"]);
NSLog(#"Err code: %d", [error code]);
}
- (void)startTheBackgroundJob:(UIImage *)img
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// wait for 3 seconds before starting the thread, you don't have to do that. This is just an example how to stop the NSThread for some time
//imageView.image = img;
noOfImages++;
// [NSThread sleepForTimeInterval:3];
// [slideImageArray addObject:img];
//[NSThread sleepForTimeInterval:1];
imageView.image = img;
if(noOfImages == 1)
{
NSLog(#"noValueif:%d",noOfImages);
// imageView.image = [slideImageArray objectAtIndex:0];
[NSThread sleepForTimeInterval:1];
// imageView.image = img;
}
else if(noOfImages >1)
{
NSLog(#"noValue:%d",noOfImages);
// numTimer =0;
// myTimer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(timerRunning) userInfo:nil repeats:YES];
// imageView.image = img;
[NSThread sleepForTimeInterval:3];
}
// [self performSelectorOnMainThread:#selector(changeImageViewImage) withObject:nil waitUntilDone:NO];
[pool release];
}
I have to run a background thread for getting image from Facebook album .i have to start a slide show with the first image collected.and i given a sleep for 3 second but it is not working.Also is there any other method for getting image from url other than the NSData *data = [NSData dataWithContentsOfURL:url]; method
Did you ever find the answer for this question?? or were u able to solve it?
And for this facebook api, are you using a personalized subclass of NSThread or using Dispatch queues?
And to answer your question if you want to fetch data in the background use performSelectorONMainThread... some facebook SDK methods require to be runned on the main thread for them to work, in my personal experience i found out that most of the data requesting is necessary to be on the mainthread using this method.
It worked for me, hopefully it works for you
:).