Class method: is that the right way to avoid memory leakage? - objective-c

First, I am new on this website, thus thank you in advance for your help.
My iPhone app has a class whose main role is to encapsulate the way I treat data on a bunch of raw bytes coming from a web server. Each time I need to display a defined type of information from that data (e.g. a piece of advice contained within these raw bytes) I call the method getAdviceFromGame. This method builds a displayable NSString by calling a NSString class method at the end (the object sent back by the stringWithUTF8String class method is autoreleased, according to method naming rules - no init, no alloc in the name). Please note that I did not put "New" in my method name because the caller does not own the object sent back by the method.
Here is the method:
-(NSString*) getAdviceFromGame:(NSInteger)number
ofLine:(NSInteger)line {
// Returns the advice at line specified as argument.
NSInteger currentOffset;
NSRange currentRange;
NSInteger offsetAdvice;
NSInteger length;
char currentCString[100];
if (line == 1)
offsetAdvice = OFF_ADVICE1;
else
offsetAdvice = OFF_ADVICE2;
// Length is the same whateve is the line.
length = LEN_ADVICE1;
// Point to the begnning of the requested game.
currentOffset = OFF_G1_SET + (number - 1) * LEN_SET;
// Point to the selected advice.
currentOffset = currentOffset + offsetAdvice;
// Skip TL
currentOffset = currentOffset + 2;
currentRange.location = currentOffset;
currentRange.length = length;
NSLog(#"Avant getBytes");
// Get raw bytes from pGame.
// Contains a C termination byte.
[pGame getBytes:currentCString range:currentRange];
// Turn these raw bytes into an NSString.
// We return an autoreleased string.
return [NSString stringWithUTF8String:currentCString];
}
This method if from my point of view, not critical, from a memory management point of view since I only send back an "autoreleased" object. Note: pGame is an internal class variable of type NSData.
In my app, in order to understand how autoreleased objects behave, I have looped 10000 times this method in the - (void)applicationDidFinishLaunching:(UIApplication *)application function and also 10000 times the same method in - (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController. This way, I can invoke big allocations.
During code execution, when the app is launched, I can see with the alloc measurement tool that the allocated object size is growing (from 400K to 800K). Then, when the applicationDidFinishLaunching method ends, then the amount gets down to 400K. Thus I guess that the pool has been "drained" by the operating system (kind of garbage management).
When I click on a tab bar, once again there is a big allocation due to the loop. Also, I can see the size growing (because thousands of NSString are allocated and sent back). When that's done, then the size gets down to 400K.
Thus my first questions are:
Q1: Can we precisely know when the autorelease pool will be "drained" or "purged"?
Q2: Does this happen at the end of OS/GUI methods such as didSelectViewController?
Q3: Must someone who calls getAdviceFromGame retain the object sent back by my method before using it?
Now I have another (more complicated) method where I internally allocate a mutable string on which i am working on before sending back the NSString:
-(NSString*) getBiddingArrayFromGame:(NSInteger)number
ofRow:(NSInteger)row
ofLine:(NSInteger)line {
NSInteger offset;
char readByte;
NSMutableString *cardSymbol = [[NSMutableString alloc] initWithString:#""];
NSRange range;
// Point to the begnning of the requested game.
offset = OFF_G1_SET + (number - 1) * LEN_SET;
// Returns the array value from cell (row, line)
// We must compute the offset of the line.
// We suppose that the offset cannot be computed, but
// only deduced from line number through a table.
switch (line) {
case 1:
offset = offset + OFF_LINE1;
break;
case 2:
offset = offset + OFF_LINE2;
break;
case 3:
offset = offset + OFF_LINE3;
break;
case 4:
offset = offset + OFF_LINE4;
break;
default:
// This case should not happen but for robustness
// we associate any extra value with a valid offset.
offset = OFF_LINE4;
break;
}
// Skip TL bytes
offset = offset + 2;
// From the offset and from the row value, we can deduce
// the offset in the selected line.
offset = offset + (row - 1);
// Now, we must read the byte and build a string from
// the byte value.
range.location = offset;
range.length = 1;
[pGame getBytes:&readByte range:range];
// We must extract the family type.
// If the family if of type "Special" then we must build by
// hand the value to display. Else, we must build a string
// with the colour symbol and associated character by reading
// in the card character table.
switch (readByte & CARD_FAMILY_MASK) {
case COLOUR_CLUBS:
// "Trèfles" in French.
[cardSymbol appendString:CLUBS_UTF16];
break;
case COLOUR_DIAMONDS:
[cardSymbol appendString:DIAMONDS_UTF16];
break;
case COLOUR_HEARTS:
[cardSymbol appendString:HEARTS_UTF16];
break;
case COLOUR_SPADES:
[cardSymbol appendString:SPADES_UTF16];
break;
case COLOUR_SPECIAL:
break;
case COLOUR_ASSET:
default:
break;
}
[cardSymbol autorelease];
// Return the string.
return [NSString stringWithString:cardSymbol];
}
As you can see, this is not very complicated but more critical from a memory management point of view since I "internally" alloc and init an NSString. Since I use it at the end of the method, I can only autorelease it before calling stringWithString:cardSymbol (in fact I would like to release it so that it is deallocated right now) else it could be deallocated before stringWithString:cardSymbol method. Well I am not satisfied with the way to do this but maybe it is the right way to do it.
Thus my last question is: Is that the right way to do it?
I am afraid that the autorelease pool be purged before reaching stringWithString:cardSymbol.
Best Regards,
Franz

First, you don't have a class method anywhere in that code, but your question title says you do have a class method. Thus, I'd suggest re-reading the Objective-C guide again (seriously -- I read that thing about once every six months for the first 5 years I programmed Objective-C and learned something every time).
Thus my first questions are: Q1: Can
we precisely know when the autorelease
pool will be "drained" or "purged"?
Q2: Does this happen at the end of
OS/GUI methods such as
didSelectViewController? Q3: Must
someone who calls getAdviceFromGame
retain the object sent back by my
method before using it?
There is no magic with autorelease pools. The documentation is quite clear on how they work.
For a UIKit based application, there is an autorelease pool managed by the run loop. Every pass through the runloop causes the release pool to be drained. If you are allocating tons of temporary objects in a single pass through the runloop, then you might need to create and drain your own autorelease pool (nothing about your code indicates that is what you need to do).
As you can see, this is not very
complicated but more critical from a
memory management point of view since
I "internally" alloc and init an
NSString. Since I use it at the end of
the method, I can only autorelease it
before calling
stringWithString:cardSymbol (in fact I
would like to release it so that it is
deallocated right now) else it could
be deallocated before
stringWithString:cardSymbol method.
Well I am not satisfied with the way
to do this but maybe it is the right
way to do it.
Unless you explicitly create and drain an autorelease pool, your string isn't going to disappear out from under you. There is nothing automatic about the draining of pools.
There is no reason to create an immutable copy of a mutable string. Just return the mutable string. If you really really really want to create an immutable copy, then do as Yuji recommended.

Q1: Can we precisely know when the autorelease pool will be "drained" or "purged"?
Q2: Does this happen at the end of OS/GUI methods such as didSelectViewController?
Yes, the autorelease pool is drained once per the event loop.
Q3: Must someone who calls getAdviceFromGame retain the object sent back by my method before using it?
If that someone wants to keep the object after that particular event cycle, yes. If not, you don't have to, because the autoreleased object is guaranteed to be alive until your current event-dealing method returns.
Please note that I did not put "New" in my method name because the caller does not own the object sent back by the method.
Very good! But I also recommend you to change the method name from getAdviceFromGame:ofLine to adviceFromGame:ofLine. Usually in Cocoa, get... is used when something is returned by a pointer passed as a method argument, just when you used [pGame getBytes:&readByte range:range];.
As for the second part, you can use instead of your lines
[cardSymbol autorelease];
// Return the string.
return [NSString stringWithString:cardSymbol];
the sequence
NSString*s=[SString stringWithString:cardSymbol];
[cardSymbol release];
return s;
or even just
return [cardSymbol autorelease];
because NSMutableString is a subclass of NSString. But just one object in the autorelease pool won't matter much, even on the iPhone.

Related

Objective-C Reusing NSString Memory Leak

I have written a very simple test application to try to help with a larger project I'm working on.
Simply put, the test app loops a predetermined number of times and appends "1" to a string on each loop. When the loop hits a multiple of 1000, the string is reset and the process starts over again.
The code is below; but what I am finding is that the memory usage is much higher than I would expect. Each iteration adds about .5MB.
It appears that the newString is not reused, but is discarded and a new instance created, without recovering the memory it was using.
Ultimately, the software needs to count much much higher than 100000.
As a test, if I change the iteration to 10million, it takes over 5GB memory!
Has anybody got any suggestions? So far I have various ways of writing the clearing of the string and turning off ARC and releasing it/recreating manually, but none seem to be reclaiming the amount of memory I would expect.
Thank you for any help!
*ps. Yes this actual software is totally pointless, but as I say, its a test app that will be migrated into a useful piece of code once fixed.
int targetCount = 100000;
NSString * newString;
int main(int argc, const char * argv[]) {
#autoreleasepool {
process();
return 0;
}
}
void process() {
for (int i=0; i<targetCount; i++) {
calledFunction(i);
}
}
void calledFunction(count) {
if ((count % 1000) == 0) {
newString = nil;
newString = #"";
} else {
newString = [NSString stringWithFormat:#"%#1", newString];
}
}
Your calledFunction function creates an autoreleased NSString that won't be released until the current autorelease pool gets drained.
Your process function calls the calledFunction 100,000 times in a loop. During the duration of this loop, the current autorelease pool is not given a chance to drain. By the time the end of the process method is reached, all 100,000 instances of the NSString objects created in calledFunction are still in memory.
A common solution to avoid a build-up of autoreleased objects when using a large loop is to add an additional autorelease pool as shown below:
void process() {
for (int i=0; i<targetCount; i++) {
#autoreleasepool {
calledFunction(i);
}
}
}
Your problem stems from the auto release pool, a somewhat anachronistic feature in the days of ARC.
When an object is created with an alloc/init combination the resultant object is owned by the caller. The same is true for the standard new method, it is defined as alloc followed by init.
For each init... method a class by have a matching <type>... method, this is defined as alloc/init/autorelease and returns an unowned object to the caller. For example your code uses stringWithFormat: which is defined as alloc/initWithFormat/autorelease.
The unowned returned object is in the auto release pool and unless ownership is taken it will be release automatically the next time that pool is emptied, which for the main autorelease pool is once per iteration of the main event loop. In many programs iterations of the event loop are frequent enough to reclaim objects from the auto release pool quick enough that memory usage does not climb. However if objects are created and then discarded a lot with a single iteration of the event loop, as in your example of a large for loop, the auto release pool can end up with a lot of needed objects before it is emptied.
A Common Solution
A common solution to this problem is to use a local auto release pool, which is emptied as soon as it is exited. By judicious placement of such local auto release pools memory use can be minimised. A common place for them is wrapping the body of loops which generate a lot of garbage, in your code this would be:
void process()
{
for (int i=0; i<targetCount; i++)
{ #autoreleasepool
{
calledFunction(i);
}
}
}
here all auto released and discarded objects created by calledFunction() will be reclaimed on every iteration of the loop.
A disadvantage of this approach is determining the best placement of the #autoreleasepool constructs. Surely in these days of automatic referencing count (ARC) this process can be simplified? Of course...
Another Solution: Avoid The Auto Release Pool
The problem you face is objects ending up in the auto release pool for too long, a simple solution to this is to never put the objects in the pool in the first place.
Objective-C has a third object creation pattern new..., it is similar to the <type>... but without the autorelease. Originating from the days of manual memory management and heavy auto release pool use most classes only implement new - which is just alloc/init - and no other members of the family, but you can easily add them with a category. Here is newWithFormat:
#interface NSString (ARC)
+ (instancetype)newWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
#end
#implementation NSString (ARC)
+ (instancetype)newWithFormat:(NSString *)format, ...
{
va_list args;
va_start(args, format);
id result = [[self alloc] initWithFormat:format arguments:args];
va_end(args);
return result;
}
#end
(Due to the variable arguments this is more involved than most new family methods would be.)
Add the above to your application and then replace calls to stringWithFormat with newWithFormat and the returned strings will be owned by the caller, ARC will manage them, they won't fill up the auto release pool - they will never enter it, and you won't need to figure out where to place #autoreleasepool constructs. Win, win, win :-)
HTH

Objective-c pendulum modelling memory issues

I am trying to implement a modelling class for a Physics project with finite difference methods for simulating a simple pendulum. I want to be able to make this class as generic as possible so I can do whatever I want with the values on each iteration of the method. For this reason I have given my methods callback blocks which can also be used to stop the method if we want to.
For example my Euler method loop looks like so:
for (NSInteger i = 0; i < n; i++) {
if (callBack) {
if(!callBack(NO, currentTheta, currentThetaDot, currentT, (CGFloat)i/n)) break;
}
currentTheta += self.dt*f_single_theta(currentThetaDot);
currentThetaDot += self.dt*f_single_thetaDot(currentTheta, currentThetaDot, gamma);
currentT += self.dt;
}
And in the callBack block I run the code
^BOOL (BOOL complete, double theta, double thetaDot, CGFloat timeElapsed, CGFloat percentComplete){
eulerT = [eulerT stringByAppendingFormat:#"%.8f\n",timeElapsed];
eulerTheta = [eulerTheta stringByAppendingFormat:#"%.8f\n",theta];
if ((currentThetaDot*currentThetaDot + cos(currentTheta)) > 0.5) {
return 0; // stops running if total E > 0.5
}
return 1;
}];
Where eulerT and eulerTheta are strings which I later save to a file. This callback method quickly results in a massive build up of memory, even for n of order 10,000 I end up with about 1Gb of RAM usage. As soon as I comment out calling the callBack block this drops right off. Is there anyway I can keep this nice functionality without the massive memory problems?
Many people who are new to Objective C do not realize the difference between [NSArray array] and [[NSArray alloc] init]. In the days before ARC, the difference was much more obvious now. Both create a new object, but the former allocates the object, assigns it to the current NSAutoreleasePool, and leaves it with a retain count of 0 while the latter allocates it and leaves it with a retain count of 1.
Objects that are assigned to an NSAutoreleasePool do not get deallocated immediately when the retain count reaches 0. Instead, they get deallocated when the OS gets time to. Generally this can be assumed to be when control returns to the current run loop, but it can also be when drain is called on the NSAutoreleasePool.
With ARC, the difference is less obvious, but still significant. Many, if not most, of the objects your allocate are assigned to an autorelease pool. This means that you don't get them back just because you're done using them. That leads to the memory usage spiking in tight loops, such as what you posted. The solution is to explicitly drain your autorelease pool, like this:
for (NSInteger i = 0; i < n; i++) {
if (callBack) {
#autoreleasepool {
if(!callBack(NO, currentTheta, currentThetaDot, currentT, (CGFloat)i/n))
break;
}
}
currentTheta += self.dt*f_single_theta(currentThetaDot);
currentThetaDot += self.dt*f_single_thetaDot(currentTheta, currentThetaDot, gamma);
currentT += self.dt;
}
You should wrap the inside of your loop in #autoreleasepool{} to clean up temporary objects.

Objective-C Returning Autoreleased Copies

A strange issue I've come across trying to understand Apple's Memory Management standards. Lets say I have a method that returns a copy without letting the user know that it is a copy.
+(Point2D*) Add:(Point2D*)a To:(Point2D*)b
{
Point2D * newPoint = [a copy];
[newPoint Add:b]; // Actually perform the arithmetic.
return [newPoint autorelease];
}
The problem is that Xcode's Analyze function flags this as an object being sent too many -autorelease calls. I'm assuming this is because -copy implicitly assumes that you are taking ownership, and thus the possibility of +0 retain count is likely. But I'm not entirely sure.
Xcode's Analyze Information
+(Point2D*) Add:(Point2D*)a To:(Point2D*)b
{
Point2D * newPoint = [a copy]; // <- 1. Method returns an Objective-C object with a +0 retain count.
[newPoint Add:b];
return [newPoint autorelease]; // <- 2. Object sent -autorelease method.
// <- 3. Object returned to caller with a +0 retain count.
// <- 4. Object over -autoreleased: object was sent -autorelease but the object has zero (locally visible) retain counts.
}
Any tips or hints on why this is happening? Unless I'm missing something, the code should work fine because the autorelease wont trigger until a safe time (i.e. it works kind of like a convenience constructor, user has time to retain.)
As per request, -copyWithZone: would be implemented as such:
-(id)copyWithZone:(NSZone *)zone
{
return [[Point2D allocWithZone:zone] initX:x Y:y Z:z];
}
Implement -copyWithZone:(NSZone*)zone in your Point class correctly (or at least please copy it here)

Copy Object Values

I have a method that receives an array and then stores this in the NSObject properties.
- (void)updatePoints:(NSArray *)pointArrayPassed
{
pointArray = pointArrayPassed;
pointCount= pointArray.count;
}
The following code works but obviously keeps the pointer of pointArrayPassed so when I can that it reflects down the call stack. However if I use a copy of the pointArrayPassed then the app starts to leak heavily!
Is there a way in the function to pass just the values as such instead of the pointer?
You can't just keep sticking copies in an iVar without releasing the current object. Otherwise you've lost the pointer to which you can send the release message - which is why it leaks all over the place.
This is a better replacement.
- (void)updatePoints:(NSArray *)pointArrayPassed
{
if (pointArray == pointArrayPassed) {
//the new array is the same as the current one. Do nothing
return;
}
[pointArray release];
pointArray = [pointArrayPassed copy];
pointCount = pointArray.count;
}
But it's not the most elegant way of doing it.
A better way is to declare pointArray as an property with copy as its memory management semantic (which is obvious as you have a mutable/immutable class cluster). And have a separate method called -pointCount which returns the count when required.

Releasing a pointer causes program to crash, even though the variable isn't used

I'm still slogging through learning memory allocation and Objective-C (my first run with Leaks was unpretty, at best). Here is a current issue, I hope everyone can understand it, I've used some pseudo-code, but I don't believe I've left anything out.
I have a Row Class set for some database stuff, we'll use this:
#interface Row : NSObject {
int rowID;
NSString *firstName;
NSString *lastName;
}
I have a getRow function in my database class that looks something like this:
-(Row *) getRow
{
rowPlaceholder = [[Row alloc] init];
...
rowPlaceholder.rowID = column1;
rowPlaceholder.firstName = column2;
rowPlaceholder.lastName = column3;
...
return [rowPlaceholder autorelease]; //<-----Problem is happening here
}
Now in my ViewController I have 2 functions of importance to this situation:
-(void) viewWillAppear:(BOOL)animated{
currentRow = [[Row alloc] init];
currentRow = getRow;
firstNamelabel.text = currentRow.firstName;
}
The second function is a button the gets the second word:
-(IBAction)btnLastName:(id)sender{
lastNamelabel.text = currentRow.lastName; //<---Crashes the program
}
So, as you can see, using currentRow against causes the program to crash, and what I can't understand is that if I don't release rowPlaceholder the program works fine. At this point why does currentRow care about the data from another function, since it already has what it want (a value from getRow()).
What's going on here?
currentRow = [[Row alloc] init];
currentRow = getRow;
You create a row, then overwrite it with... I don't even know what getRow is in this context. There's no way this is compiling without warnings. Anyway, try using:
currentRow = [[self getRow] retain];
(Although, for the sake of convention, you should call the method just row.)
Without seeing the rest of your getRow method or Row class, I can't tell why the problem would be happening there.
autorelease means that the object in question will be automatically released. When this occurs is tied to when the autorelease pool is emptied, but for the default case it's when the UI "comes up for air" -- when your call chain returns back to the UI system code. If you're going to hold something "across" such a return to UI system code, you need to have it explicitly retained. In general, making it a retained property is the best way to do this (though of course you need to clear/release the property in your dealloc method).
[And why do you do this:]
currentRow = [[Row alloc] init];
currentRow = getRow;
When you allocate and init rowPlaceholder and then return it as autoreleased, that means its effectively ready to be cleaned up by memory management unless some other object retains it.
In the line:
currentRow=getRow
you are getting a copy of the pointer to the object returned by getRow but do not appear to be retaining it. (and getRow is autoreleasing it before returning it).
If currentRow is a property of your class then you need to refer to it as this.currentRow to ensure the accessor method is called (and it must be declared as a retain property). if its not declared as a property and is just an attribute of your class then you need to retain it manually.
The first time you reference currentRow.firstName is ok because there has been no loss of control by the current execution thread so the autorelease pool hasn't cleaned up this memory yet.
But once your thread loses control and give memory management a chance to clean it up, when you come back into the the btnLastName method the memory pointed to by currentRow (which is a pointer to the original memory allocated in the rowPlaceHolder = [[row alloc] init] call) has been released.