Thotz/Thotz.html
 

componentsSeparatedByCharacterRunFromSet:

The following category on NSString complements the Leopard NSString method componentsSeparatedByCharactersInSet:, which treats each instance of a character from the separator set as a separate separator.

This method treats a run of separator-set characters as a single separator. I find this method more generally useful for my purposes – when it was originally written back in the 10.0 days, I created two versions: one that worked like the Apple/Leopard method and the one below. In the intervening years I have used my version many times, and the single-character version perhaps once.

This implementation is heavily “pooled”, because I have found that in general it is worthwhile to have loop-scope autorelease pools in methods where the loop iterations are not bounded. The ability of the memory allocator to reuse blocks seems to more than compensate for the pool overhead.

This method also follows my practice of returning exactly what the interface says -- I convert the result NSMutableArray to an NSArray using -copy. It is my contention that this can prevent subtle and hard-to-debug issues.


@implementation NSString (ComponentsSeparatedByCharacterRunFromSet)


- (NSArray *) componentsSeparatedByCharacterRunFromSet:(NSCharacterSet *) set

{

     NSAutoreleasePool * pool = [NSAutoreleasePool new];

     NSMutableArray * result = [NSMutableArray array];

     NSScanner * scanner = [NSScanner scannerWithString: self];

     NSString * chunk = nil;

     BOOL found, sepFound;

     NSAutoreleasePool * loopPool = nil;

     [scanner setCharactersToBeSkipped: nil];

     // skip any preceding separators

     sepFound = [scanner scanCharactersFromSet: set intoString: nil];

     if(sepFound)

          { // if initial separator, start with empty component

          [result addObject:@""];

          }

         

     while((found = [scanner scanUpToCharactersFromSet: set

            intoString: &chunk]) && (loopPool = [NSAutoreleasePool new]))

          {

          [result addObject:chunk];

          sepFound = [scanner scanCharactersFromSet: set intoString: nil];

          [loopPool release];

          }

         

     if(sepFound)

          { // if final separator, end with empty component

          [result addObject: @""];

          }

     result = [result copy];

     [pool release];

     result = [result autorelease];

     return result;

}

@end