2011-11-21 90 views
1

我正在研究一个iPad应用程序,该应用程序具有用于滚动浏览数据的滑块。滚动时,显示地图并更新数据。问题是,如果你滚动得足够快(或以某种方式触发竞争条件),应用程序崩溃访问僵尸NSString。我已经能够跟踪它在探查,发现这个:应用程序在绘制UILabel时崩溃 - NSString是一个僵尸

Event Type RefCt Timestamp  Size Responsible Library  Responsible Caller 
Malloc  1  01:55.166.466 16  Foundation    -[NSPlaceholderString initWithFormat:locale:arguments:] 
Autorelease <null> 01:55.166.472 0  Foundation    +[NSString stringWithFormat:] 
CFRetain 2  01:55.166.473 0  My Program    -[StateView updateVotes:] 
CFRetain 3  01:55.166.476 0  UIKit     -[UILabel setText:] 
CFRelease 2  01:55.166.504 0  My Program    -[StateView updateVotes:] 
CFRelease 1  01:55.177.661 0  Foundation    -[NSAutoreleasePool release] 
CFRelease 0  01:55.439.090 0  UIKit     -[UILabel setText:] 
Zombie  -1  01:55.439.109 0  UIKit     -[NSString(UIStringDrawing) drawAtPoint:forWidth:withFont:lineBreakMode:letterSpacing:includeEmoji:] 

我使用ARC上的iOS5,所以我在保留/释放的控制是根本。即使我是,看着上述,这是正确的。问题似乎是绘图函数和实际更改的UILabel字符串之间的竞争条件。 UILabel释放第一个字符串,因为已经设置了一个新的字符串,但绘图函数以某种方式持有对它的引用,但没有保留它。

作为说明,我没有以任何方式修改UILabel。

任何想法?

---代码添加更新:

滑块更新:

-(void)sliderValueChanged:(UISlider *)slider { 
    float position = slider.value - 1790.0f; 
    int year; 
    if(position <= 0.0f) { 
     year = 1789; 
    } else { 
     year = 1792 + (floor(position/4.0f)*4); 
    } 
    [self setYear:year]; 
} 

setYear:

-(void)setYear:(int)year { 
if (year == currentYear) { 
     // year didn't change, so don't do anything 
     return; 
    } 

    [yearLabel setText:[[NSString alloc] initWithFormat:@"%i", year]]; 
    currentYear = year; 

    [self getMapForYear:year]; 
} 

getMapForYear:

-(void) getMapForYear:(int)year { 
    [self setToMap:[historicalData objectForKey:[NSNumber numberWithInt:year]]; 
} 

setToMap:

-(void) setToMap:(HistoricalMap *)map { 
    // Label the map 
    for (State *state in [map states]) { 
     [mapController setVotes:[state votes] forState:[state abbreviation]]; 
    } 
} 

setVotes:forState:

-(void)setVotes:(NSNumber *)votes forState:(NSString *)stateAbbreviation { 

    StateView *state = [states objectForKey:stateAbbreviation]; 
    if (state == nil) { 
     NSLog(@"Invalid State Votes -- %@", stateAbbreviation); 
     return; 
    } 
    [state updateVotes:votes]; 
    [state setNeedsDisplay]; 
} 

updateVotes:

-(void)updateVotes:(NSNumber *)newVotes { 
    [self setVotes:newVotes]; 

    NSString *voteString = [[NSString alloc] initWithFormat:@"%@", newVotes]; 

    [voteLabel setText:voteString]; 
    if ([newVotes isEqual:[NSNumber numberWithInt:0]]) { 
     [[self voteLabel] setHidden:YES]; 
     [[self stateAbbreviationLabel] setHidden:YES]; 
    } else { 
     [[self stateAbbreviationLabel] setHidden:NO]; 
     [[self voteLabel] setHidden:NO]; 
    } 
} 
+0

请包括你在哪里更新数据作为滑块值发生变化的代码创建字符串。请记住,如果滑块是连续的,则可以每秒调用数百次。 – jrturton

+0

这是否有任何线程?上面显示的内容不应该像你描述的那样行为,在绘制之前将字符串释放,除非在另一个地方有一个不同的线程将地毯拉出。我不知道只有一个线程涉及到这种竞争条件。 –

回答

1

我认为你正试图滑块的运动过程中做太多。单独创建和执行核心数据提取请求看起来是过度的,更不用说更新整个GUI和一个标签屏幕了。您是否在设备上测试过这种性能?

可能值得剖析这些代码段并查看时间花在哪里。例如,您可以查看缓存提取请求或结果,或者只有在滑块已停止时,或者只在路径上每增加一个增量时,您才可以进行更新。

+0

就表现而言,它运行良好。我已将核心数据调用移出滑块更新并将其存储在控制器中。更新速度更快,但我遇到了同样的问题。 – bertuccio255

+0

我想我已经通过在核心数据管理对象中创建和存储字符串来解决问题。这种方式只要核心数据对象存在就存在字符串。感谢您的帮助。欣赏改善表现的想法。 – bertuccio255

+0

好吧,很高兴你的工作 - 它听起来像一个很酷的效果,擦洗历史! – jrturton

0

您havw几个内存泄漏部件的NSString:

[yearLabel setText:[[NSString alloc] initWithFormat:@"%i", year]]; // leak 

stringWithFormat方法,而不是

[yearLabel setText:[NSString stringWithFormat:@"%i", year]]; 
+0

谢谢你指出。我改变了它试图追踪僵尸,并没有将其切换回来。 – bertuccio255

+0

@ bertuccio255 - 其实,如果你在这里使用ARC,两者之间应该没有区别。你不应该泄漏任何东西。 –

0
[NSString stringWithFormat: **is the best way formatting the string than any other..** 
+0

正如我指出的铍的答案,在ARC下没有'+ stringWithFormat:'和'-initWithFormat:'之间的有效区别。另外,声称'+ stringWithFormat:'在所有情况下都是最好的,这是一个相当广泛的陈述。 –