issue with AlAsset library - iphone

I'm facing an issue with the ALAsset library: I have an UIView with 100 image views. When the view is loading, i'm calling a function for generating the images from the file name.
This is my class:
#interface myClass
{
NSString *fileName;
int pathId;
}
viewDidLoad
-(void)viewDidLoad
{
NSMutableArray *imageCollectionArray = [self createImage:arrayOfmyClassObject];
//Here I'm binding the 100 images in UIView using the images in imageCollectionArray
}
This is my method in which I found the issue:
- (NSMutableArray *)createImage:(NSMutableArray *)imageFileNamesArray
{
imageArray = [[NSMutableArray alloc] init];
for (int imageNameKey = 0; imageNameKey<100; imageNameKey++)
{
myClass *obj= [imageFileNamesArray objectAtIndex:imageNameKey];
if(obj.pathId == 0)
{
//Here adding the bundle image into the imageArray
[imageArray addObject:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:obj.fileName ofType:#"png" inDirectory:#"Images"]]];
}
else
{
typedef void (^ALAssetsLibraryAssetForURLResultBlock)(ALAsset *asset); typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error);
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset) {
};
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
UIImage *images;
if (iref)
{
//Here adding the photo library image into the imageArray
images = [UIImage imageWithCGImage:iref];
[imageArray addObject:images];
}
else
{
//Here adding the Nofile.png image into the imageArray if didn't find a photo library image
images = [UIImage imageNamed:#"Nofile.png"];
[imageArray addObject:images];
}
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror) {
//Here adding the Nofile.png image into the imageArray if any failure occurs
[imageArray addObject:[UIImage imageNamed:#"Nofile.png"]];
NSLog(#"booya, cant get image - %#",[myerror localizedDescription]);
};
NSURL *asseturl = [NSURL URLWithString:obj.fileName];
ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
[assetslibrary assetForURL:asseturl
resultBlock:resultblock failureBlock:failureblock];
}
}
return imageArray;
}
The problem was when I loads the view at first time the asset library images are not generating, only bundle images were displayed, if i go to any of the another view and return back to 100 image view then the asset images are generated.And works fine. The problem is the same function is not generating asset images at the first load. How can i fix this? Thanks in advance.

All methods related to ALAssetLibrary are asynchronous, so your view may complete its loading life cycle before the desired data is returned. You have to take this into account and redraw your view (or a portion of it) as needed.

Related

Memory warning while loading above 100 images from specific album of photo library

Please find the below code for fetching images from Photo library
- (void) initilizeAssetForPhotoLibrary {
if (!assets) {
assets = [[NSMutableArray alloc] init];
} else {
[assets removeAllObjects];
}
ALAssetsGroupEnumerationResultsBlock assetsEnumerationBlock = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result) {
[assets addObject:result];
}
};
ALAssetsFilter *onlyPhotosFilter = [ALAssetsFilter allPhotos];
[assetsGroup setAssetsFilter:onlyPhotosFilter];
[assetsGroup enumerateAssetsUsingBlock:assetsEnumerationBlock];
}
- (NSMutableArray *) getImagesFromPhotoLibrary {
for(int i=0; i<assets.count; i++) {
ALAsset *asset = [assets objectAtIndex:i];
ALAssetRepresentation *assetRepresentation = [asset defaultRepresentation];
UIImage *getImage = [UIImage imageWithCGImage:[assetRepresentation fullScreenImage] scale:[assetRepresentation scale] orientation:(UIImageOrientation)[assetRepresentation orientation]];
[arrImages addObject:getImage];
}
return arrImages;
}
Actually my requirement is that I need to load all image from specific album in application for creating the slideshow.
When I am loading less than 100 images then it works fine but above it gets memory warning and after that it crashed.
Please help me if anyone has different idea to load images and less memory consumption. Thanks in advanced.
Keep all the images in memory will not do, there is just not enough memory for this.
You will need to fill the array with the ALAssetRepresentation of the images and load the image only when you are ready to display it. This way you will only have the image in memory that you are really displaying.
then write down below code and let me know it is working or not!!!
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
for(int i=0; i<assets.count; i++) {
ALAsset *asset = [assets objectAtIndex:i];
ALAssetRepresentation *assetRepresentation = [asset defaultRepresentation];
UIImage *getImage = [UIImage imageWithCGImage:[assetRepresentation fullScreenImage] scale:[assetRepresentation scale] orientation:(UIImageOrientation)[assetRepresentation orientation]];
[arrImages addObject:getImage];
}
return arrImages;
});
Happy Coding...!!!!

Deallocating images created with UIGraphicsBeginImageContext

I'm creating images array for UIImageView so I can use [_myImageView startAnimating]; method. I figuree out that if I cache(preload) it like this way the animation is fluent.
But in Instruments I see, that game is deallocated but there is still too much memory allocated - I think there are some fragments of this UIGraphicsBeginContext stuff.
Am I using it right, is there some other way how to dealloc it?
Thanks in advance!
- (NSMutableArray *)generateCachedImageArrayWithFilename:(NSString *)filename extension:(NSString *)extension andImageCount:(int)count
{
_imagesArray = [[NSMutableArray alloc] init];
_fileExtension = extension;
_animationImageName = filename;
_imageCount = count;
for (int i = 0; i < count; i++)
{
NSString *tempImageNames = [NSString stringWithFormat:#"%#%i", filename, i];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:tempImageNames ofType:extension];
UIImage *frameImage = [self loadRetinaImageIfAvailable:imagePath];
UIGraphicsBeginImageContext(frameImage.size);
CGRect rect = CGRectMake(0, 0, frameImage.size.width, frameImage.size.height);
[frameImage drawInRect:rect];
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[_imagesArray addObject:renderedImage];
if (_isDoublingFrames)
{
[_imagesArray addObject:renderedImage];
}
else if (_isTriplingFrames)
{
[_imagesArray addObject:renderedImage];
[_imagesArray addObject:renderedImage];
}
NSLog(#"filename = %#", filename);
}
return _imagesArray;
}
- (UIImage *)loadRetinaImageIfAvailable:(NSString *)path
{
NSString *retinaPath = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:#"%##2x.%#", [[path lastPathComponent] stringByDeletingPathExtension], [path pathExtension]]];
if ([UIScreen mainScreen].scale == 2.0 && [[NSFileManager defaultManager] fileExistsAtPath:retinaPath] == YES)
return [[UIImage alloc] initWithCGImage:[[UIImage imageWithData:[NSData dataWithContentsOfFile:retinaPath]] CGImage] scale:2.0 orientation:UIImageOrientationUp];
else
return [UIImage imageWithContentsOfFile:path];
}
-(void) dealloc
{
[_imagesArray removeAllObjects];
_imagesArray = nil;
}
I had memory issues similar you wrote and what I would try out in your code
1) use #autoreleasepool
to wrap either the method or the content of the loop in #autoreleasepool { } to create a local pool which will be released after method is done. In your situation it seems to me wrapping the loop is better:
for (int i = 0; i < count; i++)
{
#autoreleasepool {
// the loop code
...
}}
2) kill [UIImage imageNamed]
Using imageNamed will prepare a cache (depending on the size of image) which will never be flushed out of the memory unless you reach a memory warning. Even if you set myImage.image = nil, the image is still in memory. XCode's Intruments tool Allocations view will show it nicely. What you can do is initialize UIImage like this, and iOS won't cache the image:
image = [UIImage imageWithContentsOfFile:fullpath];
You might use imageNamed in your code under loadRetinaImageIfAvailable.
Keep both two approach and let's check this out again!

Memory leak attaching multiple photos in MFMailComposerViewController

I am trying to attach multiple photos in a MailComposerViewController and I am using ALAssetPickerViewController to pick multiple photos. I have an NSMutableArray, which contains reference of selected assets. I am implementing a for loop which is enumerating over array of selected assets to get NSData of UIImage and UIImage is initialized with CGImageRef.
Code is as written below:
#autoreleasepool
{
NSString *emailTitle = #"Test";
NSString *messageBody = #"IOS programming is so fun!";
NSArray *toRecipents = [NSArray arrayWithObjects:#"abc#gmail.com", nil];
MFMailComposeViewController *tempmcvc = nil;
tempmcvc = [[MFMailComposeViewController alloc] init];
tempmcvc.mailComposeDelegate = self;
[tempmcvc setSubject:emailTitle];
[tempmcvc setMessageBody:messageBody isHTML:YES];
[tempmcvc setToRecipients:toRecipents];
tempmcvc.modalPresentationStyle=UIModalPresentationFullScreen;
tempmcvc.navigationBar.barStyle = UIBarStyleBlackOpaque;
for (AlAsset *assets in SelectedAssetsarray)
{
#autoreleasepool
{
UIImage *attachImagTemp = nil;
NSData *myData = nil;
CGImageRef iref = [assets.defaultRepresentation fullScreenImage];
NSString *nameOfImgTemp;
attachImagTemp = [UIImage imageWithCGImage:iref];
nameOfImgTemp = assets2.defaultRepresentation.filename;
myData = UIImageJPEGRepresentation (attachImagTemp, 1.0);
[tempmcvc addAttachmentData:myData mimeType:#"image/jpeg" fileName:nameOfImgTemp];
myData = nil;
attachImagTemp = nil;
iref = nil;
nameOfImgTemp = nil;
ALAsset *_temp = assets2;
_temp = nil;
}
}
}
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self presentModalViewController:tempmcvc animated:YES]
});
Each asset I am attaching hardly is of 2 MB, but memory is constantly decreasing I am unable to release the memory properly; some memory is leaking please help to find the leak.
Try releasing attachment image with following code CGImageRelease(attachImagTemp); to release cache memory instead setting it as nil.
Also release tempmcvc object as suggested by Avi if you have not used ARC.

App crashes after download of 6th image

I have a problem, need help.
I have a table, on cell I have horizontal scroll with images. Images are downloaded from internet.
When i download the 6th image, my app crashes.
For async upload I use https://github.com/rs/SDWebImage
-(void) fastCreateImage
{
int tempID = self.currentPageNow;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.2f * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
if(tempID==self.currentPageNow)
{
NSUInteger objIdx = [self.imageViews indexOfObject: [NSNumber numberWithInt:tempID]];
if(objIdx != NSNotFound) {
NSLog(#"WAS CACHED!!!!!!");
}
else
{
UIImageView *myImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 193.5f)];
NSString *urlInString =[NSString stringWithFormat:#"%#/uploads/gallery/hotels/%#",webSite,[self.urlGarbage objectAtIndex:self.currentPageNow]];
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:[NSURL URLWithString:urlInString]
delegate:self
options:0
success:^(UIImage *image, BOOL cached)
{
myImageView.image = image;
[[self.views objectAtIndex:tempID] addSubview:myImageView];
[self.imageViews addObject:[NSNumber numberWithInt:tempID]];
NSLog(#"LOADED IMG");
}
failure:nil];
[myImageView release];
}
}
});
}
Since you're getting a memory warning, I guess your images are too large to fit in memory. An image uses a maximum of width*height*4 (depending on color depth) of your memory. Calculate this memory requirement for each of your images to see if this is the problem.

fetching photo alassetlibrary asset representation size zero

When I am fetching photos using the ALAssetLibrary, for some images, the AssetRepresentation.size comes zero, which does not make the image, on my ImageView.
Here is the code:
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupAlbum usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if ([[group valueForProperty:ALAssetsGroupPropertyName] isEqual:self.groupName]) {
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop){
//Get the asset type
NSString *assetType = [result valueForProperty:ALAssetPropertyType];
if ([assetType isEqualToString:ALAssetTypePhoto]) {
NSLog(#"Photo Asset");
}
//Get URLs for the assets
NSDictionary *assetURLs = [result valueForProperty:ALAssetPropertyURLs];
NSUInteger assetCounter = 0;
for (NSString *assetURLKey in assetURLs) {
assetCounter++;
}
//Get the asset's representation object
ALAssetRepresentation *assetRepresentation = [result defaultRepresentation];
//From this asset representation, we take out data for image and show it using imageview.
dispatch_async(dispatch_get_main_queue(), ^(void){
CGImageRef imgRef = [assetRepresentation fullResolutionImage];
//Img Construction
UIImage *image = [[[UIImage alloc] initWithCGImage:imgRef] autorelease];
NSLog(#"before %#:::%lld", [image description], [assetRepresentation size]); //Prints '0' for size
if((image != nil)&& [assetRepresentation size] != 0){
//display in image view
}
else{
// NSLog(#"Failed to load the image.");
}
});
}];
}
}failureBlock:^(NSError *error){
NSLog(#"Error retrieving photos: %#", error);
}];
[library release];
Please Help. What am I doing wrong here? and how should I get the image?
Joe Smith is right! You release the library too soon. The moment the AssetLibrary is released, all the asset object will be gone with it. And Because these enumeration is block codes so the release of AssetLibrary will be executed somewhere during the reading process.
I suggest that you create your assetLibrary in the app delegate to keep it alive and only reset it when you receive change notification ALAssetsLibraryChangedNotification from the ALAssetLibrary
I think you released the assets library too soon.