0

I have an NSTimer that fires once per second.

And every second I have an NSString that needs to be changed.

I've never tried to deal with memory management before so I'm not sure if what I'm doing is right but instruments is saying under "alloc" that the line of code with stringByReplacingOccurrencesOfString has 45MB of "Live Bytes" after about a minute...

(and the live byte count keeps on rising with every second and eventually crashes the app).

I think my issue lies somewhere with the MutableCopy code?

Here is my code:

-(void)myTimer {
    if (testedit) {
        [testedit release];
        [withString1a release];
        [forString1a release];
    }
    testedit = [[NSString alloc] init];
    withString1a = [[NSString alloc] init];
    forString1a = [[NSString alloc] init];

    testedit = [[NSString alloc] initWithFormat:@"example"];
    withString1a = [[NSString alloc] initWithFormat:@"e"];//this string gets its values randomly from an array in my real code
    forString1a = [[NSString alloc] initWithFormat:@"flk34j"];//this string gets its values randomly from an array in my real code

    testedit = [[testedit stringByReplacingOccurrencesOfString:withString1a withString:forString1a]  mutableCopy];//memory leak /:

}
Albert Renshaw
  • 17,282
  • 18
  • 107
  • 195

4 Answers4

2

You are allocating memory for each object twice. When you alloc the second time and assign it to the same variable, the first piece of alloc'd memory becomes inaccessible and unreleasable.

Then you make a mutableCopy of testedit and assign the copy to the original's variable. Again, you leave a piece of inaccessible memory floating around.

The rule with non-ARC memory management is - for every alloc, new, copy or retain you need to have a corresponding release. You have 6 allocs, one copy, and only 3 releases.

Here are some suggestions.

Remove these duplicated allocations:

   testedit = [[NSString alloc] init];
   withString1a = [[NSString alloc] init];
   forString1a = [[NSString alloc] init];

Presumably testedit, withString1a and forString1a are all iVars. (Please declare your iVars as autosynthesized properties and refer to them as self.testedit ... etc. that will make your code so much clearer to stack overflowers).

Take out all of this:

if (testedit) {
        [testedit release];
        [withString1a release];
        [forString1a release];
    }

Assuming these are all iVars, the correct place to release them is in your object's dealloc method

In fact withString1a and forString1a can be local variables, as you get their content from elsewhere:

 NSString*  withString1a = [[[NSString alloc] initWithFormat:@"e"] autorelease];
 NSString*  forString1a =  [[[NSString alloc] initWithFormat:@"flk34j"] autorelease];

You can autorelease them as you don't need them to hang around after the method has finished.

These lines can also be written:

 NSString*  withString1a = [NSString stringWithFormat:@"e"];
 NSString*  forString1a =  [NSString stringWithFormat:@"flk34j"];

(-stringWithFormat is a convenience method that returns an autoreleased object)

That leaves us with these two lines.

  testedit = [[NSString alloc] initWithFormat:@"example"];
  testedit = [[testedit stringByReplacingOccurrencesOfString:withString1a 
                                                  withString:forString1a]  mutableCopy];

It's not clear why you are treating testedit as an immutable string in the first line and a mutable string in the second. You don't need a mutable string here at all, as you are replacing testedit with a new string.

 self.testedit = [[NSString alloc] initWithFormat:@"example"];
 self.testedit = [[testedit stringByReplacingOccurrencesOfString:withString1a 
                                                  withString:forString1a] copy]; 

(you need copy as stringByReplacingOccurrencesOfString:withString: returns an autoreleased object, and here you want to keep hold of it)

THE last piece of the jigsaw is getting rid of your _testedit iVar memory allocation. You do this in the dealloc method of your object:

- (void) dealloc {
    [_testEdit release];
    [super dealloc];
}

(Note that init, accessor, and dealloc methods are the three places where you should not refer to an iVar using property syntax.)

All good, but really, you should be using ARC! You are _far_more likely to introduce memory bugs this way than if you rely on the compiler to manage memory for you.

Community
  • 1
  • 1
foundry
  • 31,615
  • 9
  • 90
  • 125
  • You dont need to use `self.testedit = [[NSString alloc] initWithFormat:@"example"]; self.testedit = [[testedit stringByReplacingOccurrencesOfString:withString1a withString:forString1a] copy];`. `self.testedit = @"example";` is enough. And why do you need to do a `copy` when your are using `@properties`? Doesn't make any sense. – iDev Jan 23 '13 at 05:43
  • @ACB, I know about the redundancy, but I had assumed Albert had put that in as a placeholder for something more meaningful. If I abbreviated it the answer wouldn't have helped the question. Tx for pointing out `copy` error, corrected. – foundry Jan 23 '13 at 10:50
1

I would suggest you to make use of @property here.

In .h file declare the properties as:

@property (nonatomic, retain) NSString *testedit;
@property (nonatomic, retain) NSString *withString1a;
@property (nonatomic, retain) NSString *forString1a; //if required write the @synthesize as well in .m class

You can write your timer method as:

-(void)myTimer {

    self.testedit = @"example";
    self.withString1a = @"e";//this string gets its values randomly from an array in my real code
    self.forString1a = @"flk34j";//this string gets its values randomly from an array in my real code
    self.testedit = [self.testedit stringByReplacingOccurrencesOfString:self.withString1a withString:self.forString1a];
}

In dealloc method, you can set all the above properties as nil (self.testedit = nil;) or do a release on them([testedit release];).

If possible, try to switch to ARC, you dont have to worry about the memory management. The problem with your code was that you are using a lot of alloc/init statements without releasing the variable before doing it. This causes it to lose the reference of that variable and you will leak it. You dont need that many allocation statements. For every allocation or retain, there should be a corresponding release/auto-release statement.

iDev
  • 23,310
  • 7
  • 60
  • 85
  • I switched to ARC and I still got a memory leak with some code somewhere in my HUGE app so I'm just going through and manually managing everything. – Albert Renshaw Jan 22 '13 at 23:23
  • I'll try this. I was wondering how to get rid of that darned `mutableCopy` hehe – Albert Renshaw Jan 22 '13 at 23:23
  • 1
    @AlbertRenshaw, You dont need `mutableCopy` at all. You should have used `retain` instead even with your current code in question. – iDev Jan 22 '13 at 23:26
  • Ah! Brilliant! Okay, I added your properties and synthesized and changed mutableCopy to retain... now I'm running instruments, I'll get back to you! – Albert Renshaw Jan 22 '13 at 23:35
  • @AlbertRenshaw, With the above code, you dont need retain. I was just referring to the question where you used mutableCopy. You could have used retain there but with properties, you dont need it anymore. And dont forget to set these properties as nil in dealloc. – iDev Jan 22 '13 at 23:37
  • I was using retain because when I didn't with the above code it crashed... But I forgot to set them nil in dealloc... would that be why? – Albert Renshaw Jan 22 '13 at 23:40
  • That is because `stringByReplacingOccurrencesOfString` returns an autoreleased string and when it executes `[testedit release]` it was causing it to crash. That is why I mentioned as you should have used retain(mutableCopy also does the same) there. You should release only when you have allocated or retained and in this case, the previous testedit was leaked and this new one returned from `stringByReplacingOccurrencesOfString` was autoreleased. So it crashes. – iDev Jan 22 '13 at 23:43
  • So to fix it I use `retain]autorelease];` and I get rid of my custom release method? – Albert Renshaw Jan 22 '13 at 23:44
  • That much will not be sufficient. You have to do all the changes mentioned in my answer. – iDev Jan 22 '13 at 23:45
  • Okay, I did everything you said in your answer. Do I also get rid of my custom release method? And then I don't use `retain` or `autorelease` at all? – Albert Renshaw Jan 22 '13 at 23:46
  • Yes, with @properties, you dont need those release statements and retain/autorelease at all. Just use `self.testedit = @"example";` and it will release `testedit` and will initialize `testedit` with `@"example"`. Later in dealloc you can set to `self.testedit = nil;` which will internally release `testedit` and then set it to `testedit = nil;` – iDev Jan 22 '13 at 23:47
  • It didn't fix my allocations live bytes problem /: – Albert Renshaw Jan 23 '13 at 00:09
  • @AlbertRenshaw, Is it reduced now? You have to do this everywhere in your project. – iDev Jan 23 '13 at 00:15
  • Nope /: I don't know what else to try! It's been all day! I might just hire someone to fix my code who lives near me. – Albert Renshaw Jan 23 '13 at 00:27
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/23180/discussion-between-acb-and-albert-renshaw) – iDev Jan 23 '13 at 00:28
  • 1
    @AlbertRenshaw, All I can say is, this is the only way you can optimize the memory. I wonder if anyone else can do anything on this matter. Please follow this everywhere and check if it helps. Please accept in that case. – iDev Jan 23 '13 at 01:10
  • I got everything working now... turning on ARC still had memory problems but enabling arc along with removing some of the things like MutableCopy and a mixture of code from various answers got it working.. thank you for all of your help! God bless! – Albert Renshaw Jan 23 '13 at 05:31
  • @AlbertRenshaw, To be frank, that answer you choose as correct answer has lot of mistakes. You dont use `self.testedit = [[NSString alloc] initWithFormat:@"example"];` and it should be `self.testedit = @"example";` Not sure how that got two upvotes. – iDev Jan 23 '13 at 05:42
0

You are getting a memory leak because you never de-allocate testedit. Whenever you call alloc, that means you need to deallocate it. This usually just means calling release.

Do something like this instead, then be sure to free up the memory you've allocated:

NSString* newString = [[testedit stringByReplacingOccurrencesOfString:withString1a withString:forString1a]  mutableCopy];
Brian
  • 6,910
  • 8
  • 44
  • 82
  • How and where do I deallocate it? I tried adding `[testedit dealloc];` to the end of the NSTimer function and the app crashed. – Albert Renshaw Jan 22 '13 at 22:44
  • 1
    Never call -dealloc directly except [super dealloc] when implementing your own dealloc method. Other than that, only call -release or -autorelease – Catfish_Man Jan 22 '13 at 22:50
0

If you're using ARC you shouldn't have an issue. If you aren't using ARC you can try adding autorelease:

testedit = [[[testedit stringByReplacingOccurrencesOfString:withString1a withString:forString1a]  mutableCopy] autorelease];
pldoverflow
  • 869
  • 6
  • 10