I'm trying to learn Objective-C. I almost finished one excercise but it is not deallocating the memory:
This is is what I have:
void PrintPolygonInfo() {
NSLog(#"--------------------");
NSLog(#" PRINT POLYGON INFO");
NSLog(#"--------------------");
NSMutableArray *array = [[NSMutableArray alloc] init];
PolygonShape *p1 = [[PolygonShape alloc] initWithNumberOfSides:4 minimumNumberOfSides:3 maximumNumberOfSides:7];
PolygonShape *p2 = [[PolygonShape alloc] initWithNumberOfSides:6 minimumNumberOfSides:5 maximumNumberOfSides:9];
PolygonShape *p3 = [[PolygonShape alloc] initWithNumberOfSides:12 minimumNumberOfSides:9 maximumNumberOfSides:12];
[array addObject:p1];
[array addObject:p2];
[array addObject:p3];
// Log the descriptions
for (id shape in array) {
NSLog(#"%#", shape);
}
// Test the constraints
for (PolygonShape *shape in array) {
[shape setNumberOfSides:10];
}
[p1 release];
[p2 release];
[p3 release];
}
This is the dealloc():
- (void) dealloc {
NSLog(#"Deallocated!!!");
[super dealloc];
}
And this are the results:
2009-11-19 06:58:17.030 Assignment 1B[5441:a0f] --------------------
2009-11-19 06:58:17.030 Assignment 1B[5441:a0f] PRINT POLYGON INFO
2009-11-19 06:58:17.031 Assignment 1B[5441:a0f] --------------------
2009-11-19 06:58:17.031 Assignment 1B[5441:a0f] init: Retain count of 1.
2009-11-19 06:58:17.032 Assignment 1B[5441:a0f] init: Retain count of 1.
2009-11-19 06:58:17.032 Assignment 1B[5441:a0f] init: Retain count of 1.
2009-11-19 06:58:17.033 Assignment 1B[5441:a0f] Hello I am a 4-sided polygon (aka a Square) with angles of 90 degrees (1.570796 radians).
2009-11-19 06:58:17.033 Assignment 1B[5441:a0f] Hello I am a 6-sided polygon (aka a Hexagon) with angles of 120 degrees (2.094395 radians).
2009-11-19 06:58:17.034 Assignment 1B[5441:a0f] Hello I am a 12-sided polygon (aka a Dodecagon) with angles of 150 degrees (2.617994 radians).
2009-11-19 06:58:17.034 Assignment 1B[5441:a0f] Invalid number of sides: 10 is greater than the maximum of 7 allowed
2009-11-19 06:58:17.035 Assignment 1B[5441:a0f] Invalid number of sides: 10 is greater than the maximum of 9 allowed
As you can see, it is not printing the 'Deallocated!!!" message:
Can anyone tell me what I am doing wrong please?
Thanks in advance
The array retains the objects it contains. The objects can only be deallocated once they are removed from the array, or the array is released.
You're leaking your array. You create it with alloc/init, but never release it. Either release it, or create it with [NSMutableArray array].
Also, the dealloc method is called on objects when they're released. However, it looks like you're using a function (not a method of an object) to do that stuff. How is the rest of your code set up?
Note that if you're application terminates, the dealloc method might not be called. The application's memory is cleared anyways and OSX might decide it's faster to just terminate the app and clear the memory without going through all deallocs.
This is documented in the NSObject reference.
Related
I have a QuantumClone class which has an array of CGPoints. The single QuantumPilot object creates a QuantumClone at the beginning of each level. During the next level the QuantumPilot records its velocities to its QuantumClone. At the beginning of a new level the game loop runs this code
QuantumClone *c = [[self.pilot clone] copy];
c.bulletDelegate = self;
c.weapon = self.pilot.weapon;
[self.clones addObject:c];
But eventually the game will be reset and each QuantumClone object in the clones NSMutableArray will be removed.
Am I leaking memory by assigning values to the CGPoint pastVelocities[4551] ?
How do I reset these? I can't release them since they are not Objective-C objects. Do I need to call C functions to release this memory?
#interface QuantumClone : QuantumPilot <NSCopying> {
CGPoint pastVelocities[4551];
}
- (id)copyWithZone:(NSZone *)zone {
QuantumClone *c = [[[QuantumClone alloc] init] autorelease];
c.weapon = self.weapon;
for (NSInteger i = 0; i < 4551; i++) {
[c recordVelocity:pastVelocities[i] firing:pastFireTimings[i]];
}
[c recordLatestIndex:timeIndex];
return c;
}
- (void)recordVelocity:(CGPoint)vel firing:(BOOL)firing {
CGPoint p = pastVelocities[timeIndex];
p.x = vel.x;
p.y = vel.y;
pastVelocities[timeIndex] = p;
bool fired = firing;
pastFireTimings[timeIndex] = fired;
timeIndex++;
}
#interface QuantumPilot : CCNode {}
....
#property (nonatomic, retain) QuantumClone *clone;
- (void)copyDeltas {
[self.clone recordVelocity:ccp(self.vel.x, -self.vel.y) firing:self.firing];
}
- (void)createClone {
self.clone = [[[QuantumClone alloc] init] autorelease];
self.clone.active = YES;
self.clone.weapon = self.weapon;
}
Am I leaking memory by assigning values to the CGPoint pastVelocities[4551] ?
Short answer: No.
Long answer: The array in your code is a big chunk of contiguous memory where all CGRects live in, and it has automatic storage, which means it will be allocated and deallocated automatically (when it goes out of scope). In other words, when its parent object is destroyed, the array will be gone along with those 4551 objects.
You can verify its size by printing the result of sizeof(pastVelocities). Dividing the result by sizeof(CGRect) will tell you how many objects of this type can be stored in it.
A deallocation must be married to an explicit allocation. You only need to release memory that is allocated dynamically (explicitly), for example, using the alloc function family (malloc, calloc, realloc, etc).
How do I reset these?
memset(pastVelocities, 0, sizeof(pastVelocities));
This will reset the entire array.
jweyrich beat me to it, but I'll go ahead and post this in case it helps ;)
––
You aren't leaking. The runtime allocates enough memory to hold all ivars. In this case, each QuantumClone instance will use ~18k (~36k on 64-bit) more memory than a QuantumPilot, since you have told the runtime that it needs to allocate enough ivar storage for 4551 CGPoints.
If pastVelocities were a CGFloat * rather than a CGFloat[4551], you would need to manually allocate the memory via malloc and then call free in -dealloc. However, by declaring it as a fixed size C-array, the runtime handles it (but at the expense of making every QuantumClone a rather huge object).
That said, this whole approach seems fragile. Why 4551? Why C arrays? There is nothing wrong with using C arrays for performance, but I strongly suspect this is premature optimization.
I have a custom class called Hexagon which is a subclass from NSObject. However when I assign it to a sprite and add it to the screen by calling -addChild:, it has a retain count of 2! What should I do, in order to stop that leakage ?
for (int i =0; i < HEXCOUNT; i++){
Hexagon *nHex = [[Hexagon alloc]initWithDicitonary:hexPositions];
CCSprite *theSprite = (CCSprite*)nHex;
NSString *hexName = [NSString stringWithFormat:#"hexagon%d", i];
CGPoint location = CGPointFromString([[EXHEXAGONS objectForKey:hexName]objectForKey:#"position"]);
CGPoint nLocation = ccp(screenSize.width/2 + 68 * location.x,screenSize.height/2 + 39 * location.y);
theSprite.position = nLocation;
[self addChild:theSprite z:1 tag:i];
NSMutableDictionary *hexProperties = [EXHEXAGONS objectForKey:hexName];
[hexProperties setObject:theSprite forKey:#"realSprite"];
[EXHEXAGONS setObject:hexProperties forKey:hexName] ;
[[GameStateSingleton sharedMySingleton]setExistingHexagons:EXHEXAGONS];
[nHex release];
}
Don't rely on retainCount for anything. A retainCount of 2 doesn't mean the object is leaking. Only Instruments can tell you that.
Creating the Hexagon object with alloc/init will add a retain count of +1. Adding it as child will add +1. So depending on where you log the retainCount, it may be correct.
If you worry about memory leaks, by all means start using ARC.
First off, worrying about retain counts is not productive unless you are verifying that it is not getting deallocated when fully released from all the various objects which retain it.
Secondly, presumably you are putting the object into a NSArray, NSSet, or NSDictionary within addChild:z:tag:? So, that would bump it's retain count up by one.
You also cast your Hexagon object to a CCSprite and add it to the NSDictionary hexProperties, which will add another 1 to your retain count.
By the time you release your object at the bottom of the loop, your retain count will be at least 3. After the release it should be at least 2.
I'm a java programmer new to Objective C, so please be gentle :)
I'getting an errorr message saying EXC_BAD_ACCESS on calling release on an object:
I read documentation and threads on this site, but I see data that's confusing me
- (void) dealloc {
NSLog(#"dealloc in image Retain count: %i", [image retainCount]);
[image release];//method throwing EXC_BAD_ACCESS
..............
}
the logged retain count is: 1
In the code that is causing the dealloc I have:
UIImage *scrn = [[UIImage alloc] initWithCGImage:newImage];
NSLog(#"in after instantiation Retain count: %i", [scrn retainCount]);// logs retain count of 1
CGImageRelease(newImage);
Decoder *d = [[Decoder alloc] init];
.....
NSLog(#"in before decoding Retain count: %i", [scrn retainCount]);// logs retain count of 1
decoding = [d decodeImage:scrn cropRect:cropRect] == YES ? NO : YES;
NSLog(#"in after decoding Retain count: %i", [scrn retainCount]); // logs retain count of 2
[d release]; // this line causes invocation of dealloc on the previous code sniplet
[scrn release];
In decodeImage the following is going on:
- (BOOL) decodeImage:(UIImage *)i cropRect:(CGRect)cr {
NSLog(#"Decoder.mm.decodeImage initial Retain count i : %i retaincount image %i", [i retainCount], [image retainCount]); //logs: Decoder.mm.decodeImage initial Retain count i : 1 retaincount image 0
[self setImage: i];
NSLog(#"Decoder.mm.decodeImage after setting image Retain count i : %i retaincount image %i", [i retainCount], [image retainCount]);//logs: Decoder.mm.decodeImage after setting image Retain count i : 2 retaincount image 2
.......
return [self decode];
}
There are several things puzzeling me:
From what understood the retainCount is increased by calling retain or by instantiating a new object, not by assigningment of one var to another as is done in self setImage: i]; however i see that the retaincount is increased by one
Before calling [d release] the logged retainCount is 2, in the method dealloc the count is 1
If the count is 1, why do I get the EXC_BAD_ACCESS ???
Edit: added additional code as requested
#implementation Decoder
#synthesize image;
The setting of image is mentioned in the third code sniplet above.
Retain count can also be incremented by the system (for build-in types) and by properties that are defined with the retain or copy attributes. You're only responsible for the ones you cause (not the system retains), but don't depend on the retain count when trying to determine why you're getting EXC_BAD_ACCESS. XCode has some good build-in analysis tools that are better for tracking down access errors.
One important thing to note: your retaincount will never go below 1 even if you release when the count is 1.
See this question for good information on retaincount.
Here is what i have in my main method:
int main (int argc, const char * argv[]) {
NSString *string=[[NSString alloc]init];
NSLog(#"%# The retain count is:%d", string, [string retainCount]);
NSLog(#"Hello, World!");
return 0;
}
The output on executing this piece of code is "The retain count is :-1".
I was expecting it to give me 1 as the retain count, since i created the object using alloc.
I have similar problems when i try to retain or release the NSString objects.
When i try with NSArray, i get the retain count immediately after creation of object as 2. I do not understand the mechanism of how the objects are being treated.
Please explain!
Thanks in advance..
You shouldn't focus on the raw retain count value, as this can be a different value depending on various things. For example, in your first line you are allocating what is the equivalent of #"", that is an empty string. This is optimized by an object that cannot be deallocated and is shared wherever you're "allocating" it. The same is true for all static strings, e.g. look the value returned by [#"Hello" retainCount].
Instead you should focus on relative retain count. That is, is the retain count increased (+1), does it stay the same (+0) or is it decreased (-1). You always need to make sure that these relative value sum up to 0.
An example:
Foo *foo = [[Foo alloc] init];
[foo retain];
[foo release];
[foo autorelease];
The alloc method returns an object with +1, and retain also increses the count by 1. So by the end of line 2, we're at +2. Both release and autorelease have the effect of -1, so at the end of line 4 you're at +0 and everything is fine.
There are a few simple rules: if the method name is alloc, starts with the words copy or create then you will get an object with retain count +1. All other methods (especially those start with get) should return objects with retain count +0. If your method needs to create an object but its name implies that a +0 object should be returned, you can do this:
- (Foo *) bar {
Foo *result = [[Foo alloc] init]; // +1
return [result autorelease]; // -1
// Object lives until the autorelease pool is drained,
// but relative retain count is +0.
}
-1 is what you get if you cast UINT_MAX to a signed value. UINT_MAX is the documented retain count of objects which are never released, which includes string literal objects.
Speaking of the documentation, I suggest reading the big box about how retainCount is generally not useful.
This is my 'tick' function:
- (void) tick: (ccTime) dt
{
NSLog(#"%d",ticker);
if(fbut.Adown == YES && ticker > 4)//fbut is a button
{
elayer = [[effectsLayer alloc] init]; // each effectlayer draws a //projectile that moves forward 'x' ticks
elayer.e_playpos = glayer.playerpos; // player position
[self addChild:elayer z:2];
[mutable addObject: elayer];
[elayer release];
if(mutable.count > 20) // when there are 20 projectiles drawn, start //destroying the last one.
{
NSLog(#"mutable: %d", mutable.count);
[mutable removeLastObject];
}
ticker=0;
}
ticker++;
// . . .
this is what the running program looks like
http://screencast.com/t/LpNHL2kJIVpu
looks like more than 20..
interesting thing though, is that the array is holding steady at 20 objects. so if the objects are being 'removed' (via [mutable removeLastObject];) how come they show up on the screen?
Here's the next pickle...
Now i change init to retain( look for the *****'s )
- (void) tick: (ccTime) dt
{
NSLog(#"%d",ticker);
if(fbut.Adown == YES && ticker > 4)//fbut is a button
{
elayer = [[effectsLayer alloc] retain]; // *********each effectlayer draws //a projectile that moves forward 'x' ticks
elayer.e_playpos = glayer.playerpos; // player position
[self addChild:elayer z:2];
[mutable addObject: elayer];
[elayer release];
if(mutable.count > 20) // when there are 20 projectiles drawn, start //destroying the last one.
{
NSLog(#"mutable: %d", mutable.count);
[mutable removeLastObject];
}
ticker=0;
}
ticker++;
// . . .
And now no effect layers are being drawn, but still the NSArray holds 21 - 20 objects. all of these objects are uninitialized. so i added init to the end:
elayer = [[[effectsLayer alloc] retain] init];
now i have the same effects from before.
so i try autorelease..
same effect, lots and lots of pew pew pew's, way more than 20.
my goal is to only have 20 alowed to be drawn at a time and once the 20 are drawn they are deallocated. right now, with out deallocation, the program runs ok untill about 4 mins in when there are about 2000 e layers and the fps is about 5..
why won't the ship pew pew right?
(BTW i m using cocos2d frameworks) this is a project that is copyrighted by me, alex earley 2009.
First, [[effectsLayer alloc] retain] is terrible. Never do that. Ever. Never use an allocated object that has not been initialized. Furthermore, this will retain the object at least twice, since a call to +alloc returns a retained object, and you're retaining it again, then adding it to an array (which retains it a third time), but it's only getting released twice (when it's removed from the array and your singular release).
I suspect that the problem is with this line: [self addChild:elayer z:2]; What does this method do? Is it in charge of actually drawing the elayer onto the screen? If so, then that means its probably also retaining elayer, which means it's not getting deallocated, because you don't appear to be doing any sort of "removeChild" call when popping things out of your mutable array.
In short: just because something's no longer in your array doesn't mean it's also not on the screen.