应用程序在绘制UILabel时崩溃 - NSString是一个僵尸

时间:2011-11-21 20:00:36

标签: objective-c ios automatic-ref-counting

我正在开发一款iPad应用,它有一个用于滚动数据的滑块。滚动时,将显示地图并更新数据。问题是,如果滚动得足够快(或以某种方式触发竞争条件),应用程序在访问僵尸NSString时崩溃。我已经能够在Profiler中找到它并找到了:

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:]

我在iOS5上使用ARC,所以我根本无法控制保留/释放。即使我是,看着上面,这是正确的。问题似乎是绘图函数和实际更改的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];
    }
}

3 个答案:

答案 0 :(得分:1)

我认为你试图在滑块移动过程中做太多。单独创建和执行核心数据获取请求似乎是过度的,更不用说更新整个GUI和一系列标签。您是否在设备上测试过此功能?

可能值得分析这些代码部分并查看花费的时间。例如,您可以查看缓存获取请求或结果,或者您可能只需要在滑块停止时或仅沿路径的每n个增量进行更新。

答案 1 :(得分:0)

你有几个内存泄漏NSString:

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

使用stringWithFormat方法创建字符串

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

答案 2 :(得分:0)

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