i get the signal error EXC_BAD_ACCESS when trying to retrieve the return output from the randomBallPick method, i probably do it wrong.
NSString *temp = [self randomBallPick];
upBall1.image = [UIImage imageNamed:temp];
Array (containers) retain/release items that are added/removed.
The object will receive release when it's removed from container with removeObjectAtIndex: so you need to retain it before it is removed and possibly autorelease since you are returning it from your method.
NSString * chosenFilename =
[[[imageArray objectAtIndex:chosen] retain] autorelease];
[imageArray removeObjectAtIndex:chosen];
return chosenFilename;
OK, can you try this, please?
NSString *chosenFilename = [[imageArray objectAtIndex:chosen] retain];
[imageArray removeObjectAtIndex:chosen];
return [chosenFilename autorelease];
All you need to do is:
NSString *chosenFilename = [[imageArray objectAtIndex:chosen] retain];
Since the objectAtIndex method returns an autorelease object.
There are several things wrong with this piece of code.
You are initializing your array of names once, but then you keep removing stuff from it... I'm not sure you want to do this, otherwise you'll start returning nil exclusively (after the 38th call...). You may want to re-fill your array in that case. Here's a better version of your routine (I think):
static NSMutableArray *imageArray = nil;
if (!imageArray.count) {
if (imageArray==nil) imageArray = [[NSMutableArray alloc] init];
for (int c = 0; c < 37; c++)
{
NSString *imageName = [NSString stringWithFormat:#"ball_%i.png", c];
[imageArray addObject:imageName];
}
}
// Now we are basically sure that imageArray.count > 0
assert(imageArray.count>0);
NSUInteger chosen = arc4random() % imageArray.count;
NSString *chosenFilename = [[imageArray objectAtIndex:chosen] retain];
[imageArray removeObjectAtIndex:chosen];
return [chosenFilename autorelease];
As others have said, you have to retain then autorelease the strings you extract from the array (because the array releases them upon removal).
Note also that you should call 'retain' on the string before removing it from the array. The string is already released after the removeObjectAtIndex: call... so it's already too late to retain it then.
As soon as you remove the object from the array, its retain count is probably zero, and it will get dealloced. Try doing
return [[chosenFilename] retain] autorelease]
Related
In my iOS app, I am using a NSMutableArray, named imageMArray. I have set its getter and setter properties and instantiated it.
In viewDidLoad:
imageMArray=[[NSMutableArray alloc] initWithArray:CategoryImages];
imageMArray=[self shuffleOnlyArray:imageMArray];
In ShuffleOnlyArray Method:
NSMutableArray *destArray1 = [[NSMutableArray alloc] initWithCapacity: [sourceArray count]] ;
return destArray1;
In shuffle Method:
imageMArray=[[self shuffleOnlyArray:imageMArray] retain];
There appears to be a memory leak in the Shuffle method.
Should I release imageMArray or set it to nil? If it should be released, should it be autoreleased?
imageMArray=[[NSMutableArray alloc] initWithArray:CategoryImages];
In the above statement, you have a memoryleak.
Instead you can have like as follows.
imageMArray = [NSMutableArray arrayWithArray:CategoryImages];
In ShuffleOnlyArray Method, return the autoreleased object.
NSMutableArray *destArray1 = [[NSMutableArray alloc] initWithCapacity: [sourceArray count]] ;
return [destArray1 autorelease];
But after you get it, retain (take the ownership) the array object.
imageMArray=[[self shuffleOnlyArray:imageMArray] retain];
Edit
In shuffle method, do as follows:
NSMutableArray *imageMArray1 = [imageMArray mutableCopy];
if( imageMArray )
{
[imageMArray release];
}
imageMArray=[[self shuffleOnlyArray:imageMArray1] retain];
[imageMArray1 release];
Edit 2:
One more solution:
Use the category to shuffle as mentioned in the SO link
No need of creating new and releasing the arrays.
1 You already have a memory leak in the following lines.
imageMArray = [[NSMutableArray alloc] initWithArray:CategoryImages];
imageMArray = [self shuffleOnlyArray:imageMArray];
In the first line you create an object with retain count 1.
Then you say that your imageMArray pointer points to other object. You should release the first object, because you louse the reference to the fist object and you can not release it after you change the reference!
2 You should not use retain because your ShuffleOnlyArray method returns a retained object.
Your factory method should return an autorelease object and the caller of the factory should decide if if will retain it or not.
Hope I was clear enough
I have 100 images in my resource bundle named like image1.jpg,image2.jpg.
Basically what i am trying to do is create path names to those images dynamically inside a for loop.
While testing in simulator,the images loaded fine and the app did not crash.But while testing the app with instruments i was shocked to see the heavy memory leak that was happening while i was creating the path1 object.
I am pasting the entire method here for reference
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
for(int i=1 ; i<100 ; i++){
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSBundle mainBundle] pathForResource:str ofType:#"jpg"];
[self.arrayImages addObject:path1];
}
}
return self;
}
As i have not made use of any alloc inside the loop i dont have any ownership and hence no right to release the object.What is the reason for this memory leak??
Kindly explain the problem and provide the necessary solution in order to fix it..
As always,any help is highly appreciated..
arrayImages is retaining path1, and so if you do not release arrayImages it will leak. How are you creating arrayImages, and are you releasing it anywhere?
Edited based on comments:
Make sure you release arrayImages in your -dealloc method like so: [arrayImages release]; (note the lack of self).
There is no leak in the code you've shown.
There are (at least) two possibilities:
You have a leak in code you didn't paste into your question
Everything is fine and Instruments gave you a false-positive
Your loop will create a lot of autoreleased variables. These won't be deallocated until after the loop has finished, but that's how it's supposed to work.
The reason for the leak would be this line right here:
NSString *str = [NSString stringWithFormat:#"Century%d",i];
By using convenience methods in Objective-C, what happens in the background is the following:
NSString *str = [[[NSString alloc] initWithFormat:#"Century%d", i] autorelease];
Not using alloc/init to create a weak reference is a misconception. You are always the owner of a created object, no matter how you create it. The convenience method simply does the alloc/init and autoreleases it for you.
Here's what I would suggest you do to avoid leaking memory:
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
NSAutoreleasePool *tmpPool = [[NSAutoreleasePool alloc] init];
for(int i = 1 ; i < 100 ; i++) {
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSString alloc] initWithString:[[NSBundle mainBundle] pathForResource:str ofType:#"jpg"]];
[self.arrayImages addObject:path1];
[path1 release];
}
[tmpPool drain];
}
return self;
}
Let me know if this works better for you.
-EDIT- Allocating the path1 object and releasing it after adding to arrayImages.
I have been pulling out my hair trying to figure out why this is leaking. In my .h file I have a synthesized property nonatomic, retained NSMutableArray. In my viewDidLoad I declare it as:
self.tableData = [[NSMutableArray alloc] init];
[self.tableData removeAllObjects];
fillData(self.tableData);
Throughout my application, I call [self.tableData removeAllObjects] and then repopulate it with the fillData(self.tableData) function. This function fills up the data from a static C++ string set:
void fillData(NSMutableArray* list)
{
for (set<string>::const_iterator itr = sortedData.begin(); itr != sortedData.end(); ++itr){
[list addObject:[NSString stringWithFormat:#"%s", ((string)*itr).c_str()]];
}
}
In my dealloc method I do:
[self.tableData removeAllObjects], [self.tableData release], tableData = nil;
Where did I drop the ball? Instruments says it's in the [list addObject....] line.
Thanks
self.tableData = [[NSMutableArray alloc] init];
[self.tableData removeAllObjects];
fillData(self.tableData);
+1 retain for alloc, +1 retain for using the property's setter. You haven't balanced the +1 from alloc. If you are going to use the setter:
self.tableData = [NSMutableArray array];
fillData(self.tableData);
Note that removeAllObjects in that is completely pointless.
This is odd, too:
[self.tableData removeAllObjects], [self.tableData release], tableData = nil;
First, don't bother removing the objects. When the array is deallocated, it'll release all objects. Secondly, using the setter to call release and then immediately do a direct assignment is inconsistent. Either do:
self.tableData = nil;
Or:
[tableData release], tableData = nil;
(Note that the use of the , in all of this is also purely for your benefit -- it has no impact on generated code.)
Also, use stringWithUTF8String: and not stringWithFormat:.
Not sure if it's the leak, but this looks like it's a problem:
self.tableData = [[NSMutableArray alloc] init];
You say that tableData is a property that's retained. Try:
self.tableData = [NSMutableArray arrayWithCapacity:10];
That way the property retains it and the array itself is autoreleased. Your release in dealloc will bring the retain count back down to zero.
The problem is that your property is set as retain, and you set it to an already retained object.
You should do it like this:
// viewDidLoad
NSMutableArray *array = [[NSMutableArray alloc] init];
self.tableData = array;
[array release]; // this is important
// dealloc
self.tableData = nil; // will automatically release the array
In your dealloc, you use properties which retain the tableData again. That is not really what you want, so do:
[tableData release];
or
[self->tableData release]; // not necessary, but some prefer it.
or
self.tableData = nil; // property will handle release
No need to clear the tableData, no need to set anything to nil (you are deallocating, so nothing will access it anymore).
A beginner's problem: I have a method which puts data into a MutableArray. Potentially, this method can be called more than once and I am a bit concerned that it will leak memory as I am allocating the array every time it gets called:
indexContent = [[NSMutableArray alloc] init];
int numberOfEntries = [noteBookContent count]/3;
for (int k=0; k < numberOfEntries; k++) {
IndexItem *newItem = [[IndexItem alloc] init];
newItem.itemTitle = [noteBookContent objectAtIndex:(k*3)];
newItem.itemPage = k;
if (![[noteBookContent objectAtIndex:(k*3)] isEqualToString:#""]) {
[indexContent addObject:newItem];
}
[newItem release];
}
What will actually happen if indexContent = [[NSMutableArray alloc] init]; is called more than once? If it's bad, how can I prevent this? Should I call this, for instance, in the viewDidLoad? But how would I go about it if I try to do 'lazy-loading', i.e. only allocate indexContent if I really need it? Is there a way to check if it has already been allocated?
I am sorry if all of this is obvious, but I am struggling with this. Perhaps it's a basic concept which I haven't fully grasped yet. Thanks!
EDIT:
I have
#property (nonatomic, retain) NSMutableArray *indexContent;
in my header
If you call your function more then once you will leak memory due to the fact that you are not releasing already allocated memory from the previouse call. Simple check would be like this:
if(indexContent)
[indexContent release]
Read memory management docs from apple the will help you a lot.
if (indexContent == nil) indexContent = [NSMutableArray new]; // i screwed up logic first time. derp.
Make sure that when you release indexContent you set it to nil;
[indexContent release];
indexContent = nil;
(Unless it is the dealloc method, but probably still a good idea there)
Note that this assumes you want to re-use and potentially further fill the existing array. If not, you could removeAllObjects or you could release the existing and create anew.
Or, if an #property, you can:
self.indexContent = [NSMutableArray array]; // not +new!!
Or, in that method:
[indexContent release];
indexContent = [NSMutableArray new];
Surround code with a check for nil, if it is nil then allocate the array
//check if it has been allocated
if(indexContent == nil)
{
indexContent = [[NSMutableArray alloc] init];
int numberOfEntries = [noteBookContent count]/3;
for (int k=0; k < numberOfEntries; k++) {
IndexItem *newItem = [[IndexItem alloc] init];
newItem.itemTitle = [noteBookContent objectAtIndex:(k*3)];
newItem.itemPage = k;
if (![[noteBookContent objectAtIndex:(k*3)] isEqualToString:#""]) {
[indexContent addObject:newItem];
}
[newItem release];
}
}
It depends. Is indexContent declared as a retain #property? If so, the runtime will take care of releasing the previous array. If not, and you don't explicitly release it, then yes, it'll leak.
You should also make sure you are releasing indexContext in your dealloc method.
EDIT: As #bbum mentioned, you'll have to use dot notation. self.indexContent = <whatever>; My bad for overlooking this.
I'm making a mess of creating an object in a method and returning it to a variable. As in this post I know I should autorelease an object in this case, but when I do, it crashses.
I have written a method to create an array of images, and return this array. It looks like this:
- (NSMutableArray *)createImagesFor:(NSString *)animName withFrames:(int)numberFrames {
NSMutableArray *imageArray = [[NSMutableArray alloc] initWithCapacity:numberFrames];
for (int i = 1; i <= numberFrames; ++i) {
NSString *imageName = [[NSString alloc]initWithFormat:#"%#%i.png", animName, i];
[imageArray addObject:[UIImage imageNamed:imageName]];
[imageName release];
}
return imageArray;
}
I call it like this:
NSMutableArray *imageArray;
imageArray = [self createImagesFor:#"jumping" withFrames:2];
self.animationImages = imageArray;
[imageArray release];
However, when I run the build analyzer, it compiles but with the following complaint:
Potential leak of an object allocated on line 109
1. Method returns an Objective-C object with a +1 retain count (owning reference)
2. Object returned to caller as an owning reference (single retain count transferred to caller)
3. Object allocated on line 109 is returned from a method whose name ('createImagesFor:withFrames:') does not contain 'copy' or otherwise starts with 'new' or 'alloc'. This violates the naming convention rules given in the Memory Management Guide for Cocoa (object leaked)
I've had a look at the memory management document but other than autoreleasing the variable (which crashes it), I'm not sure where I'm going wrong. This is how I autoreleased it:
NSMutableArray *imageArray = [[[NSMutableArray alloc] initWithCapacity:numberFrames]autorelease];
I've tried retaining the *imageArray as suggested here like so:
NSMutableArray *imageArray;
[imageArray retain];
imageArray = [self createImagesFor:#"jumping" withFrames:2];
self.animationImages = imageArray;
[imageArray release];
But this also crashes.
The analyzer suggests I change the name of the method to something like 'newCreateImagesFor:withFrames:' but I don't see how this fixes things?
Thanks for the help.
Michael
You should change the last line of first block of code to return [imageArray autorelease] and get rid of the release in the second part (you normally don't ever need to release objects returned by method calls). That's what the analyzer complaints are about. However, I don't see why it would cause a crash.
How is the animationImages property defined? That might be the source of your problems.
- (NSMutableArray *)createImagesFor:(NSString *)animName withFrames:(int)numberFrames {
NSMutableArray *imageArray = [[NSMutableArray alloc] initWithCapacity:numberFrames];
for (int i = 1; i <= numberFrames; ++i) {
NSString *imageName = [[NSString alloc]initWithFormat:#"%#%i.png", animName, i];
[imageArray addObject:[UIImage imageNamed:imageName]];
[imageName release];
}
return imageArray;
}
returns an imageArray object that has a retain count of +1 but not auto-released, clang static analyser will warn you about this, and it is all to do with naming convention, because your method is not named to be like newImagesFor... or allocImagesFor... or copyImagesFor....
NSMutableArray *imageArray;
[imageArray retain];
// sending message to nil, does nothing
imageArray = [self createImagesFor:#"jumping" withFrames:2];
// imageArray has a retain count of 1
// and it is an autoreleased object
self.animationImages = imageArray;
[imageArray release];
// retain count = 0, will be dealloc'd,
// however it is already in the autorelease pool,
// it will be over-released at the end of current event run loop
When you declare imageArray, it is a null pointer to the class NSMutableArray, sending a message, in your case retain, to a null pointer in Objective-C is possible and will not throw an exception.
If you use a property accessor to cache your imageArray object, your property accessor should be declared to retain the object you assign to
#property (retain) NSMutableArray *imageArray;
Now that your method properly return an autoreleased imageArray, and you have a correct property accessor, all that's needed is
NSMutableArray *imageArray;
imageArray = [self createImagesFor:#"jumping" withFrames:2];
self.animationImages = imageArray;