UIFont fontWithName: residing on heap and never destroyed - iphone

NSMutableArray *fontsDetails=[[NSMutableArray alloc] init];
[fontsDetails addObject:[UIFont systemFontOfSize:28]];
NSArray *fontFamilies = [UIFont familyNames];
for (int i = 0; i < [fontFamilies count]; i++)
{
NSArray *fontNames = [UIFont fontNamesForFamilyName:[fontFamilies objectAtIndex:i]];
#autoreleasepool {
for (NSString *fontName in fontNames) {
[fontsDetails addObject:[UIFont fontWithName:fontName size:28]];
}
}
}
I am using this code in viewDidLoad of a controller, with this code the heap shot difference between the first and second run increases by 5mb and never comes down. (subsequent heap shots differences are lower). I am finding [UIFont fontWithName:] in the backtrace of heap shot, i ran the leak profiler and there are no leaks. All the fonts loaded are kept at heap and never destroyed. Please help me to solve this.

More likely than not, it is a cache in the system that is hanging on to the UIFont with the expectation that it will be used again.
Given that it grows once, and never again, it isn't a leak.
If you use the reference event tracker in the Allocations instrument, you can see what is retaining the font. I'd bet that you'll find an extra retain or two somewhere in the UIFont machinery.

Related

Where am I leaking memory here? Used Instruments

Instruments is saying there is a memory leak in this code:
- (void)layoutImageMaskViewForImageAtPath:(NSString *)path withFillColor:(UIColor *)color indexPath:(NSIndexPath *)indexPath {
UIImage *image = [UIImage imageWithContentsOfFile:path];
[self layoutImageMaskViewForImage:image withFillColor:color indexPath:indexPath];
}
UIColor *anIconFillColor = [UIColor colorWithWhite:0.70 alpha:1.0];
NSIndexPath *anIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
NSString *aPlaceholderPath = [[NSBundle mainBundle] pathForResource:#"path" ofType:#"png"];
[self layoutImageMaskViewForImage:anImage withFillColor:anIconFillColor indexPath:anIndexPath];
and
NSDictionary *anAssignedData = [aReservationData objectForKey:kAssignedSectionKey];
NSMutableArray *anEmployeeTaskQueueList = [NSMutableArray array];
NSArray *anAssignedReservationData = [anAssignedData objectForKey:kEmployeesIdentifier];
for (NSDictionary *aJobQueueData in anAssignedReservationData) {
EmployeeReservationQueue *anAssignedTaskQueue = [[EmployeeReservationQueue alloc] initWithServerDictionary:aJobQueueData];
if (anAssignedTaskQueue.rows.count == 0) {
ReservationTrack *aTrack = [[ReservationTrack alloc] init];
aTrack.rowSortOrder = 0;
aTrack.reservations = [NSArray array];
anAssignedTaskQueue.rows = [NSArray arrayWithObject:aTrack];
[aTrack release];
}
[anEmployeeTaskQueueList addObject:anAssignedTaskQueue];
[anAssignedTaskQueue release];
}
Your second example leaks track. Your last line is releasing aTrack instead.
In second case here:
[aTrack release];
What is aTrack? May be you mean [track release];?
In first case probably that you pass to function non-autoreleased parameters or may be you are not releasing them after calling that method. Just post code where you call for that method and I will check.
Gold memory-management rule in Objective-C :
Each 'init', 'copy','mutableCopy','retain' must call then 'release' or 'autorelease'.
Instruments reports that your app is leaking a ReservationTrack object. By default it shows where the leaked object was allocated, which is the code you posted. The code you posted doesn't leak a ReservationTrack. It stores it in an EmployeeReservationQueue which is stored in an NSMutableArray. One possibility is that you later access the ReservationTrack object, send it retain, and don't send it release or autorelease. Another possibility is that you leak the EmployeeReservationQueue or the NSMutableArray.
If you use the simulator, you can see the full retain/release history of most objects. When a leaked object shows up, mouse over the address of the object and click the right arrow that appears next to the address. Instruments will show you every malloc, retain, release, and autorelease event for that object. If you choose View > Extended Detail from the menu bar, you can click on any of those events and see the stack trace of the event. This should help you track down the unbalanced retain.

application crash after low memory warnings

my iphone application crash after raising 4 warnings of low memory, instruments is showing no memory leaks but in memory allocation live Bytes goes up to 4.7mb and Over all Bytes goes upto 79.0 MB and application crash at this point
any help will be highly appreciated
for (int i = 0; i<3; i++)
{
UIImage *rendered_image;
UIGraphicsBeginImageContextWithOptions(sub_view.bounds.size, NO, 0.0);
[appdelegate.arrimages removeAllObjects];
[appdelegate.arranimations removeAllObjects];
NSString *oldgroup = [[NSString alloc] init];
NSString *currentgroup = [[NSString alloc] init];
for(int i=0; i<[sub_view.data count]; i++)
{
oldgroup = (i>0) ? [sub_view.group objectAtIndex:(i-1)] : [sub_view.group objectAtIndex:i];
currentgroup = [sub_view.group objectAtIndex:i];
/*
IF DIFFERENT GROUP NAME RECEIVED
1-GET NEW INSTANCE OF IMAGE
2-SAVE PREVIOUS IN ARRAY
*/
if (![oldgroup isEqualToString:currentgroup])
{
rendered_image = UIGraphicsGetImageFromCurrentImageContext();
[self SaveImagesOfAnimation:[self compressImageDownToPhoneScreenSize:rendered_image]];
[appdelegate.arranimations addObject:[sub_view.anim objectAtIndex:i]];
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(sub_view.bounds.size, NO, 0.0);
}
id element = [sub_view.data objectAtIndex:i];
color = [sub_view.fillColor objectAtIndex:i];
[color setFill];
[element fill];
[[UIColor blackColor] setStroke];
[element stroke];
}
rendered_image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self SaveImagesOfAnimation:[self compressImageDownToPhoneScreenSize:rendered_image]];
}
Increasing memory use without a leak implies that you're storing data that you never release, and you're still holding a reference to it.
This usually means that one of the data structures that will grow automatically when you put more data in it, like NSMutableArray, is to blame. They will happily hold all the data you add to them and the memory profiler won't find any leaks since items put in the NSMutableArray are - by definition - never released and not detected as leaks since there's a reference to them from the array.
Edit: For the general way to solve this if you have no obvious places to look, see the comment from #Costique at the top;
Instruments can also show the kind of objects you've allocated and
the exact stack traces, so you should be able to quickly figure it
out.

Am I putting too much in my NSMutableArray? Please help!

So I am making an app really quick, and even though it isn't the best solution, it appears to be working for the most part. So I have a simple image view that pulls an image out of an NSMutableArray arr. I create the array and populate it inside of ViewDidLoad in this manner:
arr = [[NSMutableArray alloc] init];
[arr addObject:[UIImage imageNamed:#"181940jpg"]];
[arr addObject:[UIImage imageNamed:#"168026.jpg"]];
[arr addObject:[UIImage imageNamed:#"168396.jpg"]];
[arr addObject:[UIImage imageNamed:#"168493_.jpg"]];
ANd I continue doing that for 130 images. Obviously this is a big array. I don't know if this is like a really bad way to do it, but if it is, I am open to suggestions! As I go through the array with some simple back and forward buttons pulling images out of the array based on a simple counter variable things work ok until image 45-ish. The app breaks down and the console says this:
* ERROR: ImageIO 'ImageProviderCopyImageBlockSetCallback' header is not a CFDictionary...
Would it help if I broke my images up into separate arrays? Am I just putting too much into the array? What am I missing here, trust me, I am all ears.
Thanks
EDIT: Here is some more information
This is how I am sifting through the array, using a UISegmentedControl set on the top of the screen:
-(void) pickedOne{
if(segmentedControl.selectedSegmentIndex == 1){
NSLog(#"hey");
if(position < [arr count]-1){
position++;//This is my global counter variable
UIImage * img = [arr objectAtIndex:position];
[imageView setImage:img];
[img release];
}
}else if(segmentedControl.selectedSegmentIndex ==0){
if(position >0){
position--;
UIImage * img = [arr objectAtIndex:position];
[imageView setImage:img];
[img release];
}
}
}
It doesn't appear to be a problem with memory management, but then again, I don't consider myself a pro at that by any means...
In terms of the mutable array, what you put into it is just a pointer to some other object. It is those other objects you have to worry about and, yes, you have too many of them (more likely than not).
ERROR: ImageIO 'ImageProviderCopyImageBlockSetCallback'
header is not a CFDictionary...
Would it help if I broke my images up
into separate arrays? Am I just
putting too much into the array? What
am I missing here, trust me, I am all
ears.
That sounds more like you have an over-release problem and are passing something bogus to the ImageIO APIs.
And, in fact, that is exactly what you have:
UIImage * img = [arr objectAtIndex:position];
[imageView setImage:img];
[img release];
That release is spurious; it does not balance a retain anywhere in your code. objectAtIndex: does not return a retained object and the UIView will take care of retaining/releasing the image internally.
Remove that release (and the other one, too)!
You still need to worry about memory consumption. At a size of 42K each (not an unreasonable size, but entirely made up), 130 images will weigh in at ~6MB or so, prior to any decompression or other expansion that occurs as a part of storage.
The devices are quite memory constrained.

UIScrollView with UIImageView, UIImage

I've read a lot of UIScrollView with UIImageView threads here or other googled pages. But I still cannot get the problem I'm confronting. I'm having a cold right now. Hope I can still make it clear, lol. Here is the problem:
I'm building one app which mainly uses UIScrollView to display a few images. Here the amount counts, not the size, which is averagely 100KB(I even converted PNG to jpg, not sure whether it helps or not). With no more than 10 images, my app crashes with memory warning. This is the first time I encounter memory issue, which surprised me as the compiled app is less than 10MB.
At the very beginning, I load all the images on launch, looping all names of image files and do
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imgName]];
[scrollview addSubview:imageView];
[imageView release];
If I'm right, I think after launch, all the images are in memory, right? But the funny thing here is, the app could launch without any problem(at most a level 1 memory warning). After I scroll a few images, it crashed. I checked leaks for sure and also allocations. No leak and allocation almost had no change during scrolling.
So, is there something special done by imageNamed rather than cache?
And then, yes, I turned to lazy load.
For fear of checking page and loading images on demand might jerk the scrolling(which was proved true), I used a thread which runs a loop to check offset of the scroll view and load/unload images.
I subclassed UIImageView by remembering the image name. It also contains loadImage and unloadImage which will be executed on that thread.
- (void)loadImage {
/if ([self.subviews count] == 0) {
UIImageView iv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:self.imageName]];
[self performSelectorOnMainThread:#selector(renderImage:) withObject:iv waitUntilDone:NO];
//[self addSubview:iv];
[iv release];
}*/
if (self.image == nil) {
//UIImage *img = [UIImage imageNamed:self.imageName];
UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[self.imageName stringByDeletingPathExtension] ofType:[self.imageName pathExtension]]];
// image must be set on main thread as UI rendering is main thread's responsibility
[self performSelectorOnMainThread:#selector(renderImage:) withObject:img waitUntilDone:NO];
[img release];
}
}
// render image on main thread
- (void)renderImage:(UIImage*)iv {
//[self addSubview:iv];
self.image = iv;
}
(void)unloadImage {
self.image = nil;
//[(UIView*)[self.subviews lastObject] removeFromSuperview];
}
You can see the commented code that I've played with.
In unloadImage, if I write [self.image release], then I get EXC_BAD_ACCESS, which is unexpected, as I think alloc and release are matched here.
The app still crashes with no leak. The initWithContentsOfFile version even crashed earlier than imageNamed version, and made the scrolling not that smooth.
I run the app on device. By checking allocations, I found imageNamed version used much less memory than initWithContentsOfFile version, though they both crash. Instruments also showed that the allocated images were 2,3 or 4, which indicated the lazy load did do his job.
I checked PhotoScroller of WWDC2010, but I don't think it solvs my problem. There is no zooming or huge picture involved.
Anybody helps! Thank you in advance.
The crash log says nothing. The app crashes mostly after memory warning level = 2. And if run on simulator, there will be no problem.
It doesn't matter which format do you use for your images. They're converted to bitmaps when you display them.
I'd suggest to use the technique similar to that one which is used by UITableView (hide the image and free the memory it uses when it disappears from the screen and instantiate the image only when you need to show it).
As an alternate way – if you need to show these images in a grid – you might take a look to a CATiledLayer.
Anyhow, loading all the images to the memory is not the best idea :)
You can load all the images to an array. And you can design a view having one image view and try the below code:
array name: examplearray and view name :exampleview
-(void)updateImagesInScrollView
{
int cnt = [examplearray count];
for(int j=0; j< cnt; ++j)
{
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"exampleview"
owner:self
options:nil];
UIView *myView = [nibContents objectAtIndex:0];
exampleview * rview= (exampleview *)myView;
/* you can get your iamge from the array and set the image*/
rview.imageview.image = yourimage;
/*adding the view to your scrollview*/
[self.ScrollView addSubview:rview];
}
/* you need to set the content size of the scroll view */
self.ScrollView.contentSize = CGSizeMake(X, self.mHorizontalScrollView.contentSize.height);
}

ImageIO initImageJPEG instances getting allocated and never released

Im developing an app for an iPhone and I found that the following code is causing the memory allocation to increment.
-(UIImage *)createRecipeCardImage:(Process *)objectTBD atIndex:(int)indx
{
[objectTBD retain];
// bringing the image for the background
UIImage *rCard = [UIImage imageNamed:#"card_bg.png"];
CGRect frame = CGRectMake(00.0f, 80.0f, 330.0f, 330.0f);
// creating he UIImage view to contain the recipe's data
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = rCard;
[rCard release];
imageView.userInteractionEnabled = YES;
float titleLabelWidth = 150.0;
float leftGutter = 5.0;
float titleYPos = 25.0;
float space = 3.0;
float leftYPos = 0;
// locating Title label
float currentHeight = [self calculateHeightOfTextFromWidth:objectTBD.Title :titleFont :titleLabelWidth :UILineBreakModeWordWrap];
UILabel *cardTitle = [[UILabel alloc]initWithFrame:CGRectMake(leftGutter, titleYPos, titleLabelWidth, currentHeight)];
cardTitle.lineBreakMode = UILineBreakModeWordWrap;
cardTitle.numberOfLines = 0;
cardTitle.font = titleFont;
cardTitle.text = objectTBD.Title;
cardTitle.backgroundColor = [UIColor clearColor];
[imageView addSubview:cardTitle];
[cardTitle release];
leftYPos = titleYPos + currentHeight + space;
// locating brown line
UIView *brownLine = [[UIView alloc] initWithFrame:CGRectMake(5.0, leftYPos, 150.0, 2.0)];
brownLine.backgroundColor = [UIColor colorWithRed:0.647 green:0.341 blue:0.122 alpha:1.0];
[imageView addSubview:brownLine];
[brownLine release];
leftYPos = leftYPos + 2 + space + space + space;
// creating the imageView to place the image
UIImageView *processPhoto = [[UIImageView alloc] initWithFrame:CGRectMake(leftGutter, leftYPos, 150, 150)];
if((uniqueIndex == indx) && (uniqueImg.imageData != nil))
{
if([uniqueImg.rcpIden isEqualToString:objectTBD.iden])
{
objectTBD.imageData = [NSString stringWithFormat:#"%#", uniqueImg.imageData];
[recipesFound replaceObjectAtIndex:indx withObject:objectTBD];
NSData * imageData = [NSData dataFromBase64String:objectTBD.imageData];
UIImage *rcpImage = [[UIImage alloc] initWithData:imageData];
[imageData release];
processPhoto.image = rcpImage;
[rcpImage release];
}
}
else if(objectTBD.imageData != nil)
{
NSData * imageData = [NSData dataFromBase64String:objectTBD.imageData];
UIImage *rcpImage = [[UIImage alloc] initWithData:imageData];
processPhoto.image = rcpImage;
[rcpImage release];
[decodedBigImageDataPointers addObject:imageData];
}
else
{
UIImage * rcpImage = [UIImage imageNamed:#"default_recipe_img.png"];
processPhoto.image = rcpImage;
[rcpImage release];
}
NSlog(#" Process Photo Retain Count %i", [processPhoto retainCount]); // this prints a 1
[imageView addSubview:processPhoto];
NSlog(#" Process Photo Retain Count %i", [processPhoto retainCount]); // this prints a 2!!!!
//[processPhoto release]; // this line causes an error :(
// converting the UIImageView into a UIImage
UIGraphicsBeginImageContext(imageView.bounds.size);
[imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[objectTBD release];
for(UIView *eachSubview in imageView.subviews)
{
[eachSubview removeFromSuperview];
NSLog(#"each subview retainCount %i despues", [eachSubview retainCount]);
// here I find that the processPhoto view has a retain count of 2 (all other views have their retain count in 1)
}
return viewImage;
}
When I checked at the instruments object allocation I found that the "GeneralBlock-9216" growing up.
Breaking down the row I found that every time I call this code, one instance of:
2 0x5083800 00:18.534 ImageIO initImageJPEG
is being allocated. Checking the call stack, the following line is highlighted:
UIImage * objImage = [UIImage imageWithData:imageData];
Any help to find what the error is?
As TechZen said, the imageWithXXX: methods cache the image inside of them while you run the program (though you release the instances after using). I recommend initWithXXX: and release API sets instead of imageWithXXX:.
Well, if you embed several debug log on your source code, check how many times is it called, and check the retain count of the instances.
As far as I can explain, that is all.
I hope you will solve the problem.
Does anyone have an answer for this? It's tearing me apart trying to figure out why this image information keeps lingering. I've tried every solution.
The situation:
Images get downloaded and stored to the device, then loaded with imageWithContentsOfFile (or even initWithContentsOfFile, which doesn't help either). When the view goes away, the images don't, but they don't show up as leaks, they're just this initImageJPEG Malloc 9.00 KB that never goes away and keeps ramping up.
UPDATE: I believe I've figured this out: Check to make sure everything is actually getting dealloc'd when you're releasing whatever the parents (and/or grandparents) and etc of the images are. If the parents don't get deallocated, they never let go of their children images, and whatever data was in those images sticks around. So check retain counts of parent objects and make sure that everything's going away all the way up whenever you release the view at the top.
A good way to check for this is to put NSLogs into custom classes' dealloc methods. If they never show up, that object isn't going away, even though the reference to it might, and it (and whatever its subviews and properties are) will never ever disappear. In the case of images, this means a pretty sizable allocation every time that object is generated and never deallocated. It might not show up in leaks, especially if the parent of the topmost object you're thinking you're releasing but actually aren't persists and doesn't itself ever deallocate.
I hope this helps. It'll be useful to take some time to read through your code with a fine-toothed comb to make sure you're allocating and releasing things properly. (Search for "alloc]", start at the top of the file, and work your way down to make sure you're releasing and that the release isn't inside of some if() or something.)
Also, running "Build and Analyze" might lock up your machine for a bit, but its results can be really helpful.
Good luck!
I think you're seeing UIImage cacheing images. There used there used to be a method something like initWithData:cache that let you turn the cacheing off. Now I think the system always caches the images automatically behind the scenes even after you've deallocted the specific instances.
I don't think its an error on your part. I think it's the system keeping data around in the OpenGl subsystem. Unless it causes a major leak, I don't think it is a problem.