【问题标题】:App crashes on drawing of UILabel - NSString is a zombie应用程序在绘制 UILabel 时崩溃 - NSString 是僵尸
【发布时间】:2011-11-21 20:00:36
【问题描述】:

我正在开发一个 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];
}

设置年份:

-(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]];
}

设置地图:

-(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];
}

更新投票:

-(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];
    }
}

【问题讨论】:

  • 请包含您在滑块值更改时更新数据的代码。请记住,如果滑块是连续的,则每秒可以调用数百次。
  • 是否有任何线程?您在上面显示的内容不应该像您描述的那样表现,在拉出之前释放绳子,除非有不同的线程从您下方的某个地方拉出地毯。我不知道你可以有这样的竞争条件,只涉及一个线程。

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


【解决方案1】:

我认为您在滑块移动期间试图做太多事情。单独创建和执行核心数据获取请求似乎是多余的,更不用说更新整个 GUI 和一屏标签了。您是否在设备上测试过它的性能?

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

【讨论】:

  • 就性能而言,它运行良好。我已将核心数据调用从滑块更新中移出并将其存储在控制器中。更新更快,但我遇到了同样的问题。
  • 我想我已经通过在核心数据托管对象中创建和存储字符串解决了这个问题。这样,只要核心数据对象存在,字符串就存在。不过感谢您的帮助。欣赏有关提高性能的想法。
  • 好的,很高兴你让它工作了 - 它听起来确实像一个很酷的效果,擦洗历史!
【解决方案2】:

你有几个使用 NSString 的内存泄漏:

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

改为使用stringWithFormat 方法创建字符串

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

【讨论】:

  • 感谢您指出这一点。我已经改变它试图追踪僵尸并没有将它切换回来。
  • @bertuccio255 - 实际上,如果您在这里使用 ARC,则两者之间应该没有区别。你不应该泄露任何东西。
【解决方案3】:
   [NSString stringWithFormat:   **is the best way formatting the string than any other..**

【讨论】:

  • 正如我为铍的回答指出的那样,在 ARC 下,+stringWithFormat:-initWithFormat: 之间没有有效区别。此外,声称+stringWithFormat: 在所有情况下都是最好的,这是一个相当宽泛的说法。
猜你喜欢
  • 1970-01-01
  • 2015-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-18
  • 1970-01-01
  • 1970-01-01
  • 2015-07-24
相关资源
最近更新 更多