I have the following code. I get a SIGSEGV occasionally. I have a feeling I'm missing something regarding memory management using blocks. Is it safe to pass the replacedUrls, which is autoreleased to this block? What about modifying the instance variable formattedText?
NSMutableSet* replacedUrls = [[[NSMutableSet alloc] init] autorelease];
NSError *error = nil;
NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:
(NSTextCheckingTypeLink | NSTextCheckingTypePhoneNumber)
error:&error];
if (error) {
return;
}
[detector enumerateMatchesInString:self.formattedText
options:0
range:NSMakeRange(0, [self.formattedText length])
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
@try {
if (result.resultType == NSTextCheckingTypePhoneNumber) {
if (!result.phoneNumber) {
// not sure if this is possible
return;
}
self.formattedText = [self.formattedText stringByReplacingOccurrencesOfString:result.phoneNumber
withString:[NSString stringWithFormat:@"<a href=\"tel://%@\">%@</a>", result.phoneNumber, result.phoneNumber]];
}
else if (result.resultType == NSTextCheckingTypeLink) {
if (!result.URL) {
// not sure if this is possible
return;
}
NSString* fullUrl = [result.URL absoluteString];
if (!fullUrl) {
return;
}
if ([replacedUrls containsObject:fullUrl]) {
return;
}
// not sure if this is possible
if ([result.URL host] && [result.URL path]) {
NSString* urlWithNoScheme = [NSString stringWithFormat:@"%@%@", [result.URL host], [result.URL path]];
// replace all http://www.google.com to www.google.com
self.formattedText = [self.formattedText stringByReplacingOccurrencesOfString:fullUrl
withString:urlWithNoScheme];
// replace all www.google.com with http://www.google.com
NSString* replaceText = [NSString stringWithFormat:@"<a href=\"%@\">%@</a>", fullUrl, fullUrl];
self.formattedText = [self.formattedText stringByReplacingOccurrencesOfString:urlWithNoScheme
withString:replaceText];
[replacedUrls addObject:fullUrl];
}
}
}
@catch (NSException* ignore) {
// ignore any issues
}
}];
It seems that the issue you are experiencing is one related to memory management. You begin by searching through the string self.formattedText
. This means that, while this search takes place, your NSDataDetector
instance probably needs to access the string to read characters, etc. This works all fine and nice, as long as self.formattedText
doesn't get deallocated. Usually, even for block methods like this, it is the caller's responsibility to retain the arguments until the end of the function call.
When, inside of your match found block, you change the value of self.formattedText
, the old value is automatically released (assuming that this is a retain
property). I am not aware of caching that NSDataDetector might do, or issues pertaining to autorelease pools, etc., but I am pretty certain that this could cause an issue.
my suggestion is that you pass [NSString stringWithString:self.formattedText]
as the enumerateMatchesInString:
argument, rather than the plain self.formattedText
. This way, you pass the NSDataDetector an instance that won't be released until the autorelease pool is drained.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With