Reputation: 1
I recently started learning Objective-C 2.0, with a book, and I want to know if I got this concept right.
So here is the code, which causes an error for releasing an object that was not allocated:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Fraction *aFraction = [[Fraction alloc] init];
Fraction *sum = [[Fraction alloc] init], *sum2;
int n, i, pow2;
[sum setTo: 0 over: 1];
NSLog (@"Enter a value for n");
scanf ("%i", &n);
pow2 = 2;
for ( i = 1; i <= n; ++i ) {
[aFraction setTo: 1 over: pow2];
sum2 = [sum add: aFraction];
[sum release];
sum = sum2;
pow2 *= 2;
}
NSLog (@"After %i iterations, the sum is %g and the fraction is %i/%i.", n, [sum convertToNum], [sum numerator], sum.denominator);
[aFraction release];
[sum release];
[pool drain];
return 0;
}
I am wondering about sum and sum2. Here is the method add:
- (Fraction *) add: (Fraction *) f
{
Fraction *resultFraction = [[Fraction alloc] init];
int resultNum, resultDenom;
resultNum = numerator * f.denominator + denominator * f.numerator;
resultDenom = denominator * f.denominator;
[resultFraction setTo: resultNum over: resultDenom];
[resultFraction reduce];
return resultFraction;
}
Let me explain what I think is going on.
For the first iteration of the loop, sum
is allocated, then I enter the add:
method, and the resultFraction
is allocated. It is returned to sum2
, meaning that resultFraction does not take any memory after being returned.
The first sum, allocated before the loop, is released and sum = sum2
, meaning that the object "in" sum2
is now "in" sum
, and sum2
does not take any memory after the assignment. Next, a new resultFraction
is allocated and returned to sum2
, which is free until now, and so on, until sum is released after exiting the loop.
Now there is just one object (the one returned by add:
), and it is just being assigned to sum
/ sum2
(pointers? or ?). However, it is not like I thought it was -- that when sum2
is being assigned a new object (the one returned by add:
), and not being released, even after assigning that object to sum
, the previous one is still there. This means that after n assignments there will be n objects "in" sum2
. and because of that when I try to release both sum
and sum2
before pool drain I get the error. The error is from the second object I try to release, and I just can release either sum
or sum2
, because they both are connected to the last object returned by the add:
method?
I hope I was clear enough, because I've been banging my head against the wall all day long, and it just came to me and I really hope I got this right so I can continue with the book. :)
Upvotes: 0
Views: 297
Reputation: 162712
What book is that? That is some distinctly non-standard memory management patterns.
First, this method:
- (Fraction *) add: (Fraction *) f
Should be returning an auto-released object. It currently isn't. This leads to mass confusion on the calling side in that the mantra is no longer "if you want to keep an object return value (beyond NARC), you must retain it".
Next, when you see an expression like sum = sum2;
(where both variables are object references; Foo*), that is exactly like the expression x = 5;
. It is a simple numerical assignment; no retain/release implied.
Thu, if you have:
sum = [[Fraction alloc] init];
sum2 = [[Fraction alloc] init];
sum = sum2;
You've just leaked the Fraction instance that sum2 was referring to. So:
think of retains/releases as deltas; you increase or decrease the count. As long as your increases are exactly balanced with your decreases, you are doing it right.
think of the sum
in Fraction *sum;
as a potential reference to an object. When declared, it is nothing. When you assign it to the result of [[Fraction alloc] init];
, there is no magic -- sum
just holds the address of the Fraction object in memory.
Are you referring to this?
sum2 = [sum add: aFraction];
[sum release];
sum = sum2;
The release
releases the old sum
before overwriting that pointer with a reference to a new object on the next line.
Try build and analyze on that code. It will produce warnings. The book is teaching you how to do memory management using a pattern that doesn't leak, but is decidedly not standard. I'm not convinced that going down that path is useful; the reality is that you will always have autorelease in play and, thus, should always follow the standards of the system, even in your own totally isolated code. Why spend the time learning, then un-learning, a different pattern at this point? (I'm all for learning different patterns and systems... just not in this context).
Upvotes: 2