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, yourNSDataDetectorinstance probably needs to access the string to read characters, etc. This works all fine and nice, as long asself.formattedTextdoesn’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 aretainproperty). 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 theenumerateMatchesInString:argument, rather than the plainself.formattedText. This way, you pass the NSDataDetector an instance that won’t be released until the autorelease pool is drained.