Memory leak with NSString
I read almost every Question here on SO about memory management that involves NSStrings, but I can't really solve this problem.
@interface:
@property (nonatomic, retain) NSString *criticalTranscription;
@implementation: viewDidLoad:
criticalTranscription = [[NSString alloc] init];
NSArray *paragraphs = [doc valueForKeyPath:@"critical.text"];
for(int i = 0; i < [paragraphs count]; i++)
{
criticalTranscription = [criticalTranscription stringByAppendingString:[[paragraphs objectAtIndex:i] valueForKey:@"p"]];
cr开发者_如何学PythoniticalTranscription = [criticalTranscription stringByAppendingString:@"\n\n"];
}
[transcription setText:criticalTranscription];
@XIB A UISegmentedControl with an IBAction linked to:
- (IBAction) changeText:(id)sender
{
if(transcriptionSelector.selectedSegmentIndex == 1)
[transcription setText:diplomaticTranscription];
else
[transcription setText:criticalTranscription];
}
When I change the value of the UISegmentControl (first thing right after loading, nothing else runs), I run into this error (NSZombieEnabled=YES):
2011-07-07 01:10:43.639 Transcribe[404:707] *** -[CFString length]: message sent to deallocated instance 0x1189300
I can't see anything relevant in the backtrace. Without NSZombieEnabled criticalTranscription just points to random arrays or something else. There is no further usage of the variable or any releases.
I ran analyze without any suspicious leaks.
What's the problem?
The problem is that you are overwriting a reference to a string that you own with one that you don't own.
// you own the empty string returned here
criticalTranscription = [[NSString alloc] init];
NSArray *paragraphs = [doc valueForKeyPath:@"critical.text"];
for(int i = 0; i < [paragraphs count]; i++)
{
// immediately overwrite allocated instance (that you own)
criticalTranscription = [criticalTranscription stringByAppendingString:[[paragraphs objectAtIndex:i] valueForKey:@"p"]];
criticalTranscription = [criticalTranscription stringByAppendingString:@"\n\n"];
}
However, don't use this approach, because it pollutes the autorelease pool with unnecessary strings. Instead, use a mutable string and append strings to the single mutable string instance.
Also, in order to utilise the property's built-in memory management, you need to use self.criticalTranscription
and not just criticalTranscription
. Without the self.
, you are using the instance variable directly.
In your for loop
criticalTranscription = [criticalTranscription stringByAppendingString:[[paragraphs objectAtIndex:i] valueForKey:@"p"]];
criticalTranscription = [criticalTranscription stringByAppendingString:@"\n\n"];
you are setting criticalTranscription
to an autoreleased string object but not retaining it, thus the flaming death.
You could retain it or use a property with @property(nonatomic, copy)NSString *criticalTranscription;
and use the property rather than the ivar.
Two problems:
- You’re leaking the first instance of the string
- Every subsequent value you assign to it is getting autoreleased
To fix it, the easiest way is to change criticalTranscription
to an NSMutableString. Then you can do:
criticalTranscription = [[NSMutableString alloc] init];
NSArray *paragraphs = [doc valueForKeyPath:@"critical.text"];
for(int i = 0; i < [paragraphs count]; i++)
{
[criticalTranscription appendString:[[paragraphs objectAtIndex:i] valueForKey:@"p"]];
[criticalTranscription appendString:@"\n\n"];
}
[transcription setText:criticalTranscription];
…alternatively,
[criticalTranscription appendFormat:@"%@\n\n", [[paragraphs objectAtIndex:i] valueForKey:@"p"]];
Also note that you need to call release
on criticalTranscription
once you’re done with it, either at the end of your -viewDidLoad
or in its corresponding -viewDidUnload
.
精彩评论