开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜