【问题标题】:Xcode - EXEC_BAD_ACCESS when concatenting a large stringXcode - 连接大字符串时的 EXC_BAD_ACCESS
【发布时间】:2010-05-04 16:06:06
【问题描述】:

我在连接大字符串时收到 EXEC_BAD_ACCESS。

我已经阅读了一个提要并创建了我的 webview,我构建了我的字符串,例如:

NSString *pageData = @"<h1>header</h1>";

pageData = [pageData stringByAppendingFormat@"<p>"];
pageData = [pageData stringByAppendingFormat@"self.bodyText"];
pageData = [pageData stringByAppendingFormat@"</p>"];
etc

我遇到的问题是 self.bodytext 是 21,089 个带有空格的字符,当我计算单词时。 有没有更好的方法来做到这一点?

谢谢

【问题讨论】:

  • 检查您的示例代码是否存在剪切粘贴问题。您是否在“stringByAppendingFormat”之后缺少一个冒号,或者这真的是您的代码吗?此外,在第二个附加中,您是真的将“self.bodyText”放在引号内(即作为 NSString)还是实际上在做“stringByAppendingFormat:self.bodyText”,(没有@ 和引号)?

标签: objective-c xcode nsstring nsmutablestring


【解决方案1】:

你肯定想用NSMutableString 来做这样的事情:

NSMutableString * pageData = [NSMutableString stringWithCapacity:0];

[pageData appendFormat:@"<h1>header</h1>"];
[pageData appendFormat:@"<p>"];
...

NSMutableString 是为这种顺序连接而设计的,其中基本的 NSString 类实际上并不打算以这种方式使用。每次调用stringByAppendFormat: 时,您的原始代码实际上会分配一个新的 NSString,然后继续将您已经附加的数千个字符复制到其中。这很容易导致内存不足错误,因为随着您添加越来越多的调用,临时字符串的大小会呈指数增长。

当您调用appendFormat: 时,使用 NSMutableString 不会重新复制所有字符串数据,因为可变字符串维护一个内部缓冲区并简单地将新字符串附加到它的末尾。根据字符串的大小,您可能希望提前保留一大块内存(为...WithCapacity: 参数使用有意义的数字)。但除非你真的遇到性能问题,否则没有必要走那条路。

【讨论】:

  • 由于问题中的示例和答案中的示例代码实际上都没有使用格式,因此您可能应该使用 appendString: 用于 NSMutableString (请参阅developer.apple.com/mac/library/documentation/cocoa/reference/…)或 stringByAppendingString: 用于 NSString 实例(请参阅developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/…)
  • @Jason Jenkins:绝对正确。我坚持使用appendFormat: 方法,因为我假设 OP 最终会将一些动态数据插入到字符串中。否则,他也可以在文本文件中输入所有内容并避免使用可变字符串:)
  • 如果 self.bodyText 恰好包含一个看起来像格式规范的子字符串(例如 '%@')怎么办?在运行时,如果没有给出参数,这会导致提问者得到 EXEC_BAD_ACCESS 吗?
  • 是的,绝对可以。我认为您对他对“self.bodyText”的意图可能是正确的为什么不将其作为单独的答案发布?
【解决方案2】:

您的示例代码存在一些问题:

  1. 您应该使用 NSMutableString 通过附加多个部分来构建输出字符串。 NSString 是一个不可变的类,这意味着每次调用stringByAppendingFormat: 时,都会产生创建额外的新 NSString 对象的开销,该对象需要由自动释放池收集和释放。

    NSMutableString * pageData = [NSMutableString stringWithCapacity:0];

  2. 您应该在NSMutableString 上使用appendString: 来附加内容,而不是stringByAppendingFormat:appendFormat:。格式方法旨在基于包含特殊字段作为占位符的格式说明符创建新字符串。有关详细信息,请参阅Formatting String Objects。当您将stringByAppendingFormat: 与您的代码一样的文字字符串一起使用时,您会产生为不存在的占位符解析字符串的开销,更重要的是,如果字符串恰好有一个占位符(或看起来像一个)在其中,您最终会遇到EXEC_BAD_ACCESS 崩溃。当您的 bodyText 被附加时,很可能会发生这种情况。因此,如果您只是想附加一个 '

    ' 到你的NSMutableString 做这样的事情:

    [pageData appendString:@"&lt;p&gt;"];

  3. 如果您想将self.bodyText 属性的内容附加到字符串中,则不应将属性名称放在字符串文字中(即@"self.bodyText" 是文字字符串“self.body”)。 bodyText”,而不是属性的内容。试试:

    [pageData appendString:self.bodyText];

例如,您实际上可以使用格式规范来组合示例代码的所有三行:

pageData = [pageData stringByAppendingFormat:@"<p>%@</p>", self.bodyText];

在格式规范中%@ 是一个占位符,表示插入发送descriptiondescriptionWithLocale: 消息到对象的结果。对于NSString,这只是字符串的内容。

【讨论】:

    【解决方案3】:

    我怀疑字符串的长度真的是个问题。一个 50,000 个字符的字符串只有大约 100 KB。但是您要非常小心使用格式字符串。如果你的字符串包含一些看起来像格式说明符的东西,最好有一个相应的参数,否则如果你很幸运,你会得到垃圾,如果你不幸运,就会崩溃。我怀疑这是错误,因为您的描述没有其他明显的问题。请注意您在其中放入的内容,并避免将动态文本放入格式字符串中 - 只需在格式字符串中放入 %@ 并将动态文本作为参数传递。

    【讨论】:

      【解决方案4】:

      在处理任意字符串时使用 appendString: 而不是 appendFormat:。

      pageData = [pageData stringByAppendingString:@"<p>"];
      pageData = [pageData stringByAppendingString:@"self.bodyText"];
      pageData = [pageData stringByAppendingString:@"</p>"];
      

      或者不要使用任意字符串作为格式:

      pageData = [pageData stringByAppendingFormat:@"<p>%@</p>" , @"self.bodyText"];
      

      如果您要分段构建字符串,请使用 NSMutableString 而不是多个 stringBy 调用。

      请记住 % 是格式化字符串和 url 转义的特殊字符,因此如果 bodyText 包含 url 很容易导致崩溃。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-06-23
        • 2010-11-10
        • 1970-01-01
        • 1970-01-01
        • 2021-01-26
        • 2014-03-31
        • 2023-03-29
        相关资源
        最近更新 更多