Xcode instruments generated lots of __CFCachedURLResponse memory leak - iphone

I have not used any NSURL API in my code but Xcode Instruments said my code has a lot of __CFCachedURLResponse memory leaks.
For instance,
Leaked Object # Address Size Responsible Library Responsible Frame
__CFCachedURLResponse,10 < multiple > 320 Bytes IPhoneUIBase
-[InterestGroupStockTR_RTS composeRTSData:]
The code is that after creating adding couple of objects to array, then call custom class.
NSMutableArray *arrCodes = [[NSMutableArray alloc] init];
for(int j = 0; j < countCode; j++ )
{
NSDictionary *pData = [listData objectAtIndex:j];
NSString *stockCode = [ NSString stringWithFormat:#"%#", pData ];
NSMutableDictionary *codeData2 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:stockCode , #"acCode" , NULL ];
[arrCodes addObject:codeData2];
[codeData2 release];
}
[trData.dict setObject:arrCodes forKey:#"MULTI_SBR_ARRAYRP"];
[self requestWithData:trData];
[arrCodes removeAllObjects];
[arrCodes release];
Another question is Whats the difference between __CFCachedURLResponse and __CFURLCacheNode?. Some of classes have __CFURLCacheNode memory leak according to instrument.
Thanks

Related

Trying to find leak of type NSMutableArray. Instruments shows leak in method.

I eliminated all the leaks from my current app. However Instruments constantly tells me that I have a leak in the method shown below.
The leak is of type NSMutableArray and has a size of either 16 or 32 bytes. Yes, I know that's not much but it adds up. Also see it as an academic question that I need to solve to make my code leakless.
+ (id) meterFromDict:(NSDictionary*)dict {
Meter* resMeter = [[Meter alloc] initWithType:[[dict objectForKey:#"MeterBase"] intValue]];
//NSLog(#"dict: %#",dict);
resMeter.volume = nil;
resMeter.sounds = nil;
resMeter.repeats = nil;
resMeter.volume = [[[NSMutableArray alloc] initWithArray:[dict objectForKey:#"volumeArray"]] autorelease];
resMeter.sounds = [[[NSMutableArray alloc] initWithArray:[dict objectForKey:#"soundsArray"]] autorelease];
resMeter.repeats = [[[NSMutableArray alloc] initWithArray:[dict objectForKey:#"repeatsArray"]] autorelease];
//NSLog(#"MeterFromDict called and resmeter.repeats count is : %i",[resMeter.repeats count]);
resMeter.bpm = [[dict objectForKey:#"BPM"] floatValue];
return [resMeter autorelease];
}
Without looking at your Instruments output directly I can't tell exactly, but you're writing some redundant code: Try this:
+ (id) meterFromDict:(NSDictionary*)dict {
Meter* resMeter = [[Meter alloc] initWithType:[[dict objectForKey:#"MeterBase"] intValue]];
//NSLog(#"dict: %#",dict);
resMeter.volume = [dict objectForKey:#"volumeArray"];
resMeter.sounds = [dict objectForKey:#"soundsArray"];
resMeter.repeats = [dict objectForKey:#"repeatsArray"];
//NSLog(#"MeterFromDict called and resmeter.repeats count is : %i",[resMeter.repeats count]);
resMeter.bpm = [[dict objectForKey:#"BPM"] floatValue];
return [resMeter autorelease];
}
There's no point in nilling your properties before assigning new values to them.
Also, No point creating new arrays for arrays that you already have. And if you have properly declared your volume, sounds and repeats properties with copy instead of retain.
Try that and see if it works better.

Creating a long NSString causing memory issues

My code below is causing my app to quit i.e. get black screen and then see in debugger console: Program received signal: “0”.
Basically it is causing problem when my orderArray has count of 2000 or more. I am using iPhone 3GS with iOS 4.2
Question: Is there a more efficient and less memory consuming way to create my long outStr?
NSString *outStr = #"";
for (int i = 0; i < count; i++) {
NSDictionary *dict = [[ARAppDelegate sharedAppDelegate].orderArray objectAtIndex:i];
outStr = [outStr stringByAppendingFormat:#"%#,%#,%#,%#\n",
[dict valueForKey:#"CODE"],
[dict valueForKey:#"QTY"],
[[ARAppDelegate sharedAppDelegate].descDict valueForKey:[dict valueForKey:#"CODE"]],
[[ARAppDelegate sharedAppDelegate].priceDict valueForKey:[dict valueForKey:#"CODE"]]];
}
Update: Thanks to very kind people who helped, below is my modified code:
NSArray *orderA = [ARAppDelegate sharedAppDelegate].orderArray;
NSDictionary *descD = [ARAppDelegate sharedAppDelegate].descDict;
NSDictionary *priceD = [ARAppDelegate sharedAppDelegate].priceDict;
NSMutableString *outStr = [[[NSMutableString alloc] init] autorelease];
for (int i = 0; i < [orderA count]; i++) {
NSDictionary *dict = [orderA objectAtIndex:i];
NSString *code = [dict valueForKey:#"CODE"];
[outStr appendFormat:#"%#,%#,%#,%#\n",
code,
[dict valueForKey:#"QTY"],
[descD valueForKey:code],
[priceD valueForKey:code]];
}
[self emailTxtFile:[NSString stringWithString:outStr]];
// This reaches end of method
The problem is that in every iteration a new string object is formed. This consumes a lot of memory. One solution could be to use a local autoreleasepool, but that's rather complicated here.
You should use an NSMutableString, like:
NSMutableString *outStr = [[[NSMutableString alloc] init] autorelease];
for (int i = 0; i < count; i++) {
NSDictionary *dict = [[ARAppDelegate sharedAppDelegate].orderArray objectAtIndex:i];
[outStr appendFormat:#"%#,%#,%#,%#\n",
[dict valueForKey:#"CODE"],
[dict valueForKey:#"QTY"],
[[ARAppDelegate sharedAppDelegate].descDict valueForKey:[dict valueForKey:#"CODE"]],
[[ARAppDelegate sharedAppDelegate].priceDict valueForKey:[dict valueForKey:#"CODE"]]];
}
Then you can use outStr, just as if it was an NSString. As Tom points out in the comments, you could turn the NSMutableString into an NSString when you're finished, using:
NSString *result = [NSString stringWithString:outStr];
[outStr release]; // <-- add this line and remove the autorelease
// from the outStr alloc/init line
making your code re-usable and easier to maintain.

Big time Leaking in Objective-C Category

I created a custom NSString Category which lets me find all strings between two other strings. I'm now running into the problem of finding that there are a lot of kBs leaking from my script. Please see code below:
#import "MyStringBetween.h"
#implementation NSString (MyStringBetween)
-(NSArray *)mystringBetween:(NSString *)aString and:(NSString *)bString;
{
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
NSArray *firstlist = [self componentsSeparatedByString:bString];
NSMutableArray *finalArray = [[NSMutableArray alloc] init];
for (int y = 0; y < firstlist.count - 1 ; y++) {
NSString *firstObject = [firstlist objectAtIndex:y];
NSMutableArray *secondlist = [firstObject componentsSeparatedByString:aString];
if(secondlist.count > 1){
[finalArray addObject:[secondlist objectAtIndex:secondlist.count - 1]];
}
}
[autoreleasepool release];
return finalArray;
}
#end
I admit that I'm not super good at releasing objects, but I had believed that the NSAutoreleasePool handled things for me.
The line that is leaking:
NSMutableArray *secondlist = [firstObject componentsSeparatedByString:aString];
Manually releasing secondlist raises an exception.
Thanks in advance!
No, this is the line that is leaking:
NSMutableArray *secondlist = [[NSMutableArray alloc] init];
And it isn't that big of a leak (just an empty mutable array). Still, don't do that.
In particular, the line:
secondlist = [[firstlist objectAtIndex:y] componentsSeparatedByString:aString];
Is assigning over the reference to the empty mutable array.
Also FinalArray should be named finalArray.
finalArray is leaking. You should autorelease it before returning it but make sure you do it either before allocating the autorelease pool or after releasing it.

Where's the memory leak in my iPhone app?

It's a simple app that takes 30 pics from my Resources folder, puts them into an NSMutableArray during viewDidLoad, and then changes the pic based on accelerometer data. I have a UIImageView from IB covering the whole view, and when the accelerometer updates its info, it swaps the uiimageview.image to a diff pic. Works for about 10 seconds, and then crashes with this:
Program received signal: “0”.
Data Formatters temporarily unavailable, will re-try after a 'continue'. (Unknown error loading shared library "/Developer/usr/lib/libXcodeDebuggerSupport.dylib")
After looking around on here it sounds like it's a memory leak. Here's the code I'm using:
Interface:
{
UIAccelerometer *accel;
IBOutlet UIImageView *pic;
NSMutableArray *picArr;
}
Implementation:
- (void)viewDidLoad
{
[super viewDidLoad];
picArr = [[NSMutableArray alloc] init]; //array of pics to be used
NSString *path = [[NSString alloc] init]; //dynamic path of images to save to array
for (int i = 1; i <= 30; i++)
{
path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"nativity_test_%i", i] ofType:#"jpg"];
[picArr addObject:[[UIImage alloc] initWithContentsOfFile:path]];
}
accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = 0.5f;
[path release];
pic.image = [picArr objectAtIndex:15];
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
if(acceleration.y < 0.0f) //goes thru the tilted left pics
{
for (int i = 0; i <= 15; i++)
{
if( acceleration.y >= (-(15.0f-(double)i)/30.0f) && acceleration.y < (-(14.0f-(double)i)/30.0f) )
{
pic.image = [picArr objectAtIndex:i+1];
}
}
}
else if(acceleration.y > 0.0f) //goes thru tilted right pics
{
for (int i = 1; i <= 14; i++)
if( acceleration.y > ((double)i/30.0f) && acceleration.y <= ((double)(i+1)/30.0f) )
{
pic.image = [picArr objectAtIndex:i+15];
}
}
}
Please let me know if I left out any pertinent info.
This is not a crash caused by a memory leak, but by an overrelease of memory. Your crash is caused by this line:
[path release];
Remove that line and you should be fine. For some values of fine, anyway; most of what George says also applies.
The reason is that the path being released will be this one:
path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"nativity_test_%i", i] ofType:#"jpg"];
That's an object with an effective retain count of zero. While it has a positive retain count, it's already been autoreleased and will be released when you return to the main event loop.
You should also remove this line:
NSString *path = [[NSString alloc] init]; //dynamic path of images to save to array
The reason is you're assigning the result of pathForResource to path, never actually using it. You'll need to declare path as a variable, of course. I suggest changing the pathForResource line to this:
NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"nativity_test_%i", i] ofType:#"jpg"];
This is a smart change, as there's no reason you'd need this outside of the loop.
You should also fix this line:
[picArr addObject:[[UIImage alloc] initWithContentsOfFile:path]];
[UIImage alloc] returns an UIImage with an effective retain count of 1. Adding it to an array will increase that to two. If you're just releasing the array later, this will cause a memory leak. Instead:
[picArr addObject:[[[UIImage alloc] initWithContentsOfFile:path]] autorelease];
That will mean you're adding an UIImage with an effective retain count of 0 to the array, which will bump it to 1, making the array the only owner of the object. Later when the array goes away, so will most of the images. (The imageView will also own whichever is assigned to it.)
Don't be frustrated by this stuff: one day soon you'll just suddenly get it and wonder why it ever caused you headaches. :)
There's a couple of problems here:
1) You aren't releasing the UIImage objects you're adding to "picArr".
This line:
[picArr addObject:[[UIImage alloc] initWithContentsOfFile:path]];
Should be something like this instead:
UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];
[picArr addObject: image];
[image release];
I believe you could also do:
[picArr addObject: [[[UIImage alloc] initWithContentsOfFile:path] autorelease]];
2) You don't need to do NSString *path = [[NSString alloc] init]; You can just do:
NSString *path;
You are giving the path variable a value inside that first loop so there's no need to alloc+init. Once you change this you can get rid of this line:
[path release];
3) I don't see where you are releasing "picArr" itself, you'll need to. You may be doing it already but didn't include that bit of code in your post.

NSArray to NSMutableArray as random stack

Just a conceptual description first:
I am reading input from a text file (a list of words) and putting these words into an NSArray using componentsSeparatedByString method. This works.
But I wanted to select the words randomly and then delete them from the array so as to ensure a different word each time. Of course, you cannot change the NSArray contents. So...
I copied the contents of the NSArray into an NSMutableArray and use IT for the selection source. This also works - 269 objects in each array.
To return a word from the NSMutableArray I use the following code:
note- the arrays are declared globally
as
arrMutTextWords = [[NSMutableArray alloc] init]; //stack for words
arrTextWords = [[NSArray alloc] init]; //permanent store for words
-(NSString*) getaTextWord
{
// if the mutable text word array is empty refill
if ([arrMutTextWords count] == 0){
for (int i = 0 ; i < [arrTextWords count]; i++)
[arrMutTextWords addObject:[arrTextWords objectAtIndex:i]];
}
int i = random() % [arrMutTextWords count];
NSString* ptrWord = [arrMutTextWords objectAtIndex:i];
[arrMutTextWords removeObjectAtIndex:i];
return ptrWord;
}
The program crashes during a call to the method above - here is the calling code:
arrTmp is declared globally arrTmp = [[NSArray alloc] init]; //tmp store for words
for (int i = 0 ; i < 4; i++) {
tmpWord = [self getaTextWord];
[arrTmp addObject:tmpWord];
[arrTmp addObject:tmpWord];
}
I'm thinking that somehow deleting strings from arrMutTextWords is invalidating the NSArray - but I can't think how this would occur.
One possible source for problems is your fetching AND removing the NSString object from your list. Removing it releases that NSString instance therefore devalidating your reference.
To be shure to retain a reference you should use this code sequence instead:
NSString * ptrWord = [[[arrMutTextWords objectAtIndex:i] retain] autorelease];
[arrMutTextWords removeObjectAtIndex:i];
return ptrWord;
By the way: You should use
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray: array];
instead of copying all values by hand. While i do not know the implementation of NSMutableArray, i know from times long ago (NeXTstep), that there are several possible optimizations that may speed up basic NSArray operations.
And finally copying this way is much more concise.
Just ran this through XCode and got random words returned, however I skipped the whole for loop and used addObjectsFromArrayfrom NSMutableArray.
NSArray *randomArray = [[NSArray alloc] initWithObjects:#"Paul", #"George", #"John", nil];
NSMutableArray *muteArray = [[NSMutableArray alloc] init];
[muteArray addObjectsFromArray:randomArray];
int i = random() % [muteArray count];
NSString* ptrWord = [muteArray objectAtIndex:i];
[muteArray removeObjectAtIndex:i];
NSLog(#"ptrWord %#", ptrWord); //gave me a different name each time I ran the function.
Hope this clears some things up.