I have created an app which has a library of images. I have imported the images in to my resources folder in Xcode and am currently loading them in to an array in the appDelegate when the app loads. This is happening each time. I am now worried that as the library grows the app will run out of memory. Is there a better way of loading these?
the structure is
Library > category > image list > image
I have an NSMutableArray called mainLibrary which I then add another nsmutablearray for each category. For example
NSMutableArray *arrHome = [[NSMutableArray alloc] initWithObjects: #"Home",
[NSDictionary dictionaryWithObjectsAndKeys: [NSString stringWithFormat:#"%#/%#", resourcePath, #"alarm_clock.png"], #"image_path", #"Alarm Clock", #"title", nil],
[NSDictionary dictionaryWithObjectsAndKeys: [NSString stringWithFormat:#"%#/%#", resourcePath, #"bathtub.png"], #"image_path",#"Bathtub", #"title", nil],
nil];
[mainLibrary addObject: arrHome];
The above is for the "home" category and contains only two images. For other categories, I have the NSDictionary line for each image - there can be up to 100 images in each category.
If I am loading in 50mb+ of images to an array will this cause the app to become unresponsive - or more than likely, simply crash as it uses more of the 24mb (ish) allowance per app??
I am wondering whether I should do the above code and read in to the array ONCE a category has been selected - but that might then cause it to stall for the user.
Any thoughts?!
ps) I also need to have a think about Retina display. With 600+ images, duplicating them would mean my binary size would rocket!!
A while ago I ran some tests for an app I was working on. Loading 5000 images in succession:
[UIImage imageWithData:...] // 44.8 seconds
[UIImage imageWithContentsOfFile:...] // 52.3 seconds
[[UIImage alloc] initWithContentsOfFile:…] // 351.8 seconds
[UIImage imageNamed:...] // hung due to caching
Using the paths to the files is obviously the way to go. Obviously memory management will be key in your case.
compose your path dictionaries/arrays using strings, then load/unload images as needed (load lazily), if you need such a structure to interface with in your program.
600 HQ images will exceed the memory limits, and it's wasteful because the user will see how many of these images at a given time? resources are quite limited, and hq images require a lot of space.
if you have to display a bunch of thumbnails, those may be shrunk and exported easily enough so you can display all the images in a reasonable amount of time, while using a rational amount of memory (again, just load lazily where possible and release the image when not very easily viewed).
Why not use [UIImage imageNamed:IMAGE_NAME] directly ? It has a good memory management inside.
Related
I am handling with the ALAssetsLibrary.
When I get all the thumbnails, I just use the UIImageViews to hold the thumbnails and add them to the holder.
Problem is here, it is really slow to add them. Maybe ten seconds or more. If there is much photos, it will be slower.
I would want to know what is the best practice to hold these thumbnails. (Many thanks!)
Use AlAsset aspectRatioThumbnail instead of fullResolutionImage for high performance
The class ALAsset has two methods to get thumbnails:
- (CGImageRef)thumbnail
- (CGImageRef)aspectRatioThumbnail
example:
//ALAssetsLibrary block will execute in a separate thread. So I suggest to do the UI related stuff in main thread.
dispatch_sync(dispatch_get_main_queue(), ^{
CGImageRef iref = [myasset aspectRatioThumbnail];
itemToAdd.image = [UIImage imageWithCGImage:iref];
});//end block
I think in this https://github.com/johnil/JFImagePickerController project with two classes JFAssetHelper and JFImageManager you will find answer.This use NSCache to cache photos and it really quick
Actually I am using the stackoverflow for the first time to search solution to my problem.
The problem is that I am using a table view in which in each cell there is an ImageView to show a selected image and a button which opens the image picker controller and allows the user to pick the image from gallery or camera. After selecting the image from the gallery I used the function
NSData *imageData=UIImageJPEGRepresentation(image2, .4);
to convert it into data to reduce the memory size of image and then save it in the documents directory and finally reload the table.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *imagePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.png",currentCameraRow]];
if ([[NSFileManager defaultManager] fileExistsAtPath:imagePath])
{
NSLog(#"file is exiting and starting");
if ([[NSFileManager defaultManager] removeItemAtPath:imagePath error:nil]) {
NSLog(#"file is exiting and deleting");
}
}
Else I am saving the image in the documents directory and reload the UITable.
After reloading the table, the image should appear in the ImageView of the cell.
Now the problem is that if I again select the image for the same cell and load it after clicking on the camera button, the image will be appear, but the memory will also increase by .15MB which I checked on the Instruments and this makes my app crash on iPhone 3.
What I am actually thinking is that, if I select the image for the same cell irrespective of the number of times I do it, the image should be overwritten in the documents directory and the memory size should not increase when I check in the instrument but this is not happening.
I am really frustrated with this problem, so please anyone help. Your help will be highly appreciated.
If your memory footprint grows, you are most likely leaking some objects, i.e. not releasing them properly. You may check with instruments and the static analyzer.
Please note that compressing the image will not reduce the memory it will consume when loaded. This is just pixels_width * pixels_height * 4 bytes.
Even if you compress the image down so it doesn't use much storage space on disk in a file, when you load it in your app it becomes uncompressed and will take up a considerable amount of memory in the application depending on the resolution.
Try checking for zombies using instruments. There is possibly a leak resulting in memory increase. Also using cache might save you some space when choosing image again and again. Release the objects as soon as they are not needed to free up the memory used.
I am developing a quiz app. I take questions from a xml file, parse it and display random questions. These are stored in NSdictionary and NSMutableArray. Also the app plays background music and sound for clicking of buttons(AVAudioPlayer). and vibration( AudioServicesPlaySystemSound(kSystemSoundID_Vibrate))
In one particular function if i try to release the temp variables that I use(I.E NSDictionary and NSMutableArray) the app crashes wen i reach that function for the second time. Hence if i don release these, it works fine but eventually crashes with a "EXC_BAD_ACCESS" ERROR. It does not point out to any line or function.
When i used the tool "LEAKS", it showed i was having around 7000 leaks. I don understand how to use that tool but I am sure that i am not creating so many variables, jus a few and even those I release.
And just once i got the ERROR " data formatters temporarily unavailable".
Any Idea what i am doing wrong?? F1 :)
PS: my code is all simple stuff, plus i donno what the problem is hence i donno what code to post here?
Also i would like to know if i create a NSString* in a function for temp use should i release it at the end of the function?(i do release it)
EDIT:
-(void) loadQuestion
{
strCorrectAnswer = #"";
int intQuestionNo;
NSString *strQuestionNo = [[NSString alloc] init];
// get random question out the xml file
NSDictionary *dctQue = [dctQuestions objectForKey:strQuestionNo];
// blah blah
// jumble the answers to be displaed
NSMutableArray *answerJumble = [[NSMutableArray alloc] init];
NSMutableArray *answers =[NSMutableArray arrayWithObjects:[dctQue objectForKey:#"WrongAnswer1"],[dctQue objectForKey:#"WrongAnswer2"],[dctQue objectForKey:#"WrongAnswer3"],[dctQue objectForKey:#"CorrectAnswer"],nil];
// blah blah
/*
[strQuestionNo release];
[answers release];
[answerJumble release]; */
}
You should read something about memory management in Cocoa. See the Mac Developer Center or the tutorial at Cocoa Dev Central. Memory management on iPhone is not hard, it’s a pity to code by trial and error.
well after a bit of digging, the problem was wen the sound file had to be replayed. If i press a button and before the sound file finishes playing if i press again, the sound file was being played just once. Resulting in a memory leak of 3000.
IF i did this twice the app wud crash after a leak of 6425. Hence the ERROR-"data formatters currently not available".(i guess)
I'm building an app which includes a number of image sequences (5 sequences with about 80 images each). It runs nicely in the iPhone simulator, but causes my iPhone to reboot when I test it. By the way, each png image is about 8k in size.
Has anyone successfully built a similar app?
Am I using too many resources for the iPhone to handle?
Anyone?
UPDATE:
Thanks to all for you answers! I've modified my code to use [UIImage imageWithContentsOfFile:] instead of [UIImage imageNamed:]
However I'm still unable to prevent the app from crashing my iPhone.
(please note that my pngs are not that big about 400x400px / 8k)
Does anyone have any suggestions?
Here's my code:
// code snippet:
myFrames = [[NSMutableArray alloc] initWithCapacity:maxFrames];
NSMutableString *curFrame;
num = 0;
// loop (maxframes = 80)
for(int f = 1; f < maxFrames+1; f++)
{
curFrame = [NSMutableString stringWithString:tName];
if(f < 10) [curFrame appendString:[NSString stringWithFormat:#"00%i",f]];
else if(f>9 && f<100) [curFrame appendString:[NSString stringWithFormat:#"0%i",f]];
else [curFrame appendString:[NSString stringWithFormat:#"%i",f]];
UIImage *img = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:curFrame ofType:#"png"]];
if(img) [myFrames addObject:img];
[img release];
}
// animate the images!
self.animationImages = myFrames;
self.animationDuration = (maxFrames * .05); // Seconds
[self startAnimating];
The best way to find out is to run the application under Instruments using Leaks or Object Alloc. If you see an upward trend that keeps rising, you might have a leak.
If you're using [UIImage imageNamed:], you should be aware that it pre-caches an optimized version which takes up more memory when compared with [UIImage imageWithContentsOfFile:]. Additionally, until iPhone 3.0, the cache created by [UIImage imageNamed:] doesn't get released when there's a memory warning.
The current-gen iPhone only has 128MB of ram, some of which is used by the OS itself. A 320x480 image fully uncompressed with an alpha channel can take 614k. If you have 400 unique images that are full screen, that's well over 128MB of ram, assuming it is loaded up and cached uncompressed.
The number one reason why an app would not crash on the simulator but on the phone would be memory
On the iphone simulator AFAIK the memory is not limited to 128Mb while on the iphone once it reaches 128Mb it restarts. So check your memory usage on the simulator. You have to change the way you are loading the images and or check for leaks. Also check if your getting low memory warnings by implementing the methods (I forgot what they are called :()
I've seen apps run in the simulator and not on the phone because of improper PNG formatting (even a single improperly formatted image can cause this crash). Check to make sure that the format of your images matches those of PNG files provided by apple in their example apps.
That being said 400 full screen images would easily cause it to run out of memory as in memory they will occupy far more than the 8kb. Not sure how big those images are, but if they're all in memory they will need to be very, very small on the iPhone.
The first answer to your question states that while your PNGs may take up only 8K on disk, that is the compressed on-disk form. When it is loaded into memory, it is decompressed and is much larger than 8K. At 32-bits per pixel, a 400x400 image will be 640K.
Even without the alpha channel, you're looking at 480K. 480K x 80 frames, that is 38.4MB, which is definitely creeping into using more memory than the iphone has available to give your app at once. Here is an article about some of the troubles with obtaining a substantial about of memory from the iPhone OS.
So far I've managed to create an app for iPhone that takes multiple images with about a 3 second interval between each. I`m processing each image in a separate thread asynchronously and everything is great till it gets to the moment for saving the image on the iPhone disk. Then it takes about 12 seconds to save the image to the disk using JPEG representation.
How does Apple do it, how do they manage to save a single image so fast to the disk is there a trick they are using? I saw that the animations distract the user for a while, but still the time needed is below 12 seconds!
Thanks in advance.
Actually apple uses its kernal driver AppleJPEGDriver, It is a hardware jpeg encoding api and is much faster than software encoding (JPEGRepresnetaion) and some of the people using it in their jailbreak apps(cycorder video recording application).
Apple should give the same functionality to its users but they are apple :)
I haven't tried this but I wouldn't be so sure that Apple isn't using the same methods. A big part of the Apple design philosophy relies on hiding operational interruptions from the user. The Apple code may take as much time as yours but simply be adroit at hiding the entire save time from the perception of the user.
If someone can't tell you how Apple actually does save faster I would suggest looking at ways to disguise the save time.
If you google around a bit... there is a whole bunch of people with the same problem.
I didn't find an answer. The general conclusion seems to be that apple either uses some internal api and bypass public api overhead or some hardware encoder.
Guess you are out of luck for fast image saving
I was having this problem in my app, on saving it would hang so I used Grand central dispatch.
Below is the setImage method out of my image cache class, if UIImage has a image it saves it otherwise it deletes it. You can adapt this to suit your needs hopefully, will only work on iOS 4+. The code is ARC enabled.
-(void)setImage:(UIImage *)image{
if (image == nil){
NSLog(#"Deleting Image");
// Since we have no image let's remove the cached image if it exists
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask, YES) objectAtIndex:0];
[[NSFileManager defaultManager] removeItemAtPath:[cachePath
stringByAppendingPathComponent:#"capturedimage.jpg"] error:nil];
});
}
else {
NSLog(#"Saving Image");
// We've got an image, let's save it to flash memory.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *cachePath =
[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask, YES) objectAtIndex:0];
NSData *dataObj = UIImagePNGRepresentation(image);
[dataObj writeToFile:[cachePath
stringByAppendingPathComponent:#"capturedimage.jpg"] atomically:NO];
});
}
imageCache = image;
}