Xcode - EXEC_BAD_ACCESS when concatenting a large string
I'm getting a EXEC_BAD_ACCESS when concatenting a large string.
I've read from a feed and to create my webview I bu开发者_StackOverflow社区ild up my string like:
NSString *pageData = @"<h1>header</h1>";
pageData = [pageData stringByAppendingFormat@"<p>"];
pageData = [pageData stringByAppendingFormat@"self.bodyText"];
pageData = [pageData stringByAppendingFormat@"</p>"];
etc
The problem I've got is self.bodytext is 21,089 characters with spaces when I do a count on word. Is there a better method for doing this?
Thanks
You would definitely want to use NSMutableString for something like this:
NSMutableString * pageData = [NSMutableString stringWithCapacity:0];
[pageData appendFormat:@"<h1>header</h1>"];
[pageData appendFormat:@"<p>"];
...
NSMutableString
is designed for this kind of sequential concatenation, where the basic NSString class is really not meant to be used in this manner. Your original code would actually allocate a new NSString every time you called stringByAppendFormat:
, and then procede to copy into it all of the thousands of characters you had already appended. This could easily result in an out of memory error, since the size of the temporary strings would be growing exponentially as you add more and more calls.
Using NSMutableString will not re-copy all of the string data when you call appendFormat:
, since the mutable string maintains an internal buffer and simply tacks new strings on to the end of it. Depending on the size of your string, you may want to reserve a huge chunk of memory ahead of time (use a meaningful number for the ...WithCapacity:
argument). But there is no need to go that route unless you actually run into performance issues.
There are a few problems with your sample code:
You should be using a NSMutableString to build up an output string by appending multiple parts. NSString is an immutable class which means that each time you call
stringByAppendingFormat:
you are incurring the overhead of creating an additional new NSString object which will need to be collected and released by the autorelease pool.NSMutableString * pageData = [NSMutableString stringWithCapacity:0];
You should use
appendString:
on yourNSMutableString
to append content, instead ofstringByAppendingFormat:
orappendFormat:
. The format methods are intended for creating new strings based on a format specifier which includes special fields as placeholders. See Formatting String Objects for more details. When you're usingstringByAppendingFormat:
with just a literal string like your code has, you are incurring the overhead of parsing the string for the non-existant placeholders, and more importantly, if the string happens to have a placeholder (or something that looks like one) in it, you'll end up with theEXEC_BAD_ACCESS
crash that you are getting. Most likely this happening when your bodyText is appended. Thus if you simply want to append a '' to your
NSMutableString
do something like this:[pageData appendString:@"<p>"];
If you want to append the contents of the
self.bodyText
property to the string, you shouldn't put the name of the property inside of a string literal (i.e. @"self.bodyText" is the literal string "self.bodyText", not the contents of the property. Try:[pageData appendString:self.bodyText];
As an example, you could actually combine all three lines of your sample code by using a format specification:
pageData = [pageData stringByAppendingFormat:@"<p>%@</p>", self.bodyText];
In the format specification %@
is a placeholder that means insert the result of sending the description
or descriptionWithLocale:
message to the object. For an NSString
this is simply the contents of the string.
I doubt the length of the string is really a problem. A 50,000-character string is only about 100 KB. But you want to be very careful about using format strings. If your string contains something that looks like a formatting specifier, there had better be a corresponding argument or you'll get garbage if you're lucky and a crash if you're not. I suspect this is the error, since there is no other obvious problem from your description. Be careful about what you put in there, and avoid ever putting dynamic text in a format string — just put a %@
in the format string and pass the dynamic text as an argument.
Use appendString: instead of appendFormat: when dealing with arbitrary strings.
pageData = [pageData stringByAppendingString:@"<p>"];
pageData = [pageData stringByAppendingString:@"self.bodyText"];
pageData = [pageData stringByAppendingString:@"</p>"];
or do not use an arbitrary string as the format:
pageData = [pageData stringByAppendingFormat:@"<p>%@</p>" , @"self.bodyText"];
If you are building the string up in pieces, use NSMutableString instead of several stringBy calls.
Remember that % is a special character for formatted strings and for url escapes, so if bodyText contains a url it could easily cause a crash.
精彩评论