Is this a reasonable Objective-C block implementation?

I need a variant of the NSRegularExpression method – stringByReplacingMatchesInString:options:range:withTemplate: which takes a block instead of a template. The return value of the block will be used as the replacement value. As you can imagine, this is more flexible than a template. Similar to using the /e modifier in Perl regular expressions.

So, I wrote a category to add a method. Here is what I came up with:

 @implementation NSRegularExpression (Block) - (NSString *)stringByReplacingMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range usingBlock:(NSString* (^)(NSTextCheckingResult *result))block { NSMutableString *ret = [NSMutableString string]; NSUInteger pos = 0; for (NSTextCheckingResult *res in [self matchesInString:string options:options range:range]) { if (res.range.location > pos) { [ret appendString:[string substringWithRange:NSMakeRange(pos, res.range.location - pos)]]; } pos = res.range.location + res.range.length; [ret appendString:block(res)]; } if (string.length > pos) { [ret appendString:[string substringFromIndex:pos]]; } return ret; } @end 

This is my first attempt to play with blocks in Objective C. It's a bit weird, but it seems to work well. I have a few questions about this:

  • Does this seem like a reasonable way to implement such a method?
  • Is there a way to implement my internal functions with -enumerateMatchesInString:options:range:usingBlock: :? I tried, but could not assign pos from inside the block. But if there was a way to make it work, it would be great to pass NSMatchingFlags and BOOL as well and process them the same way as this method. Doable?

Update

Thanks to the answer from Dave DeLong, I have a new version using a block:

 @implementation NSRegularExpression (Block) - (NSString *)stringByReplacingMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range usingBlock:(NSString * (^)(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop))block { NSMutableString *ret = [NSMutableString string]; __block NSUInteger pos = 0; [self enumerateMatchesInString:string options:options range:range usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) { if (match.range.location > pos) { [ret appendString:[string substringWithRange:NSMakeRange(pos, match.range.location - pos)]]; } pos = match.range.location + match.range.length; [ret appendString:block(match, flags, stop)]; }]; if (string.length > pos) { [ret appendString:[string substringFromIndex:pos]]; } return [NSString stringWithString:ret]; } @end 

Works great, thanks!

+4
source share
1 answer

The ability to assign pos from within the block will be as simple as changing the declaration:

 NSUInteger pos = 0; 

To:

 __block NSUInteger pos = 0; 

More information about the __block keyword: __block Variables

+6
source

Source: https://habr.com/ru/post/1332568/


All Articles