Method for finding all classes This was inspired by a Stack Overflow question about getting all the classes of an application and I took it a little too seriously (or perhaps not seriously enough).

It's rather silly to do it this way, but I believe the code below is guaranteed to find every class exposed in the scope it is called from...eventually. (As long as the class name is 255 chars or less.)

I'm looking for improvements on my coding style, hardcore criticism, and anything that would optimize this code.

application:didFinishLaunchingWithOptions

NSString * set = @"_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
NSInteger setLength = 255;
NSLog(@"Here we go!");
[Enumerator enumerateAllStringCombinationsInSet:set toMaximumLength:setLength withBlock:^(NSString *string) {
    Class class = NSClassFromString(string);
    if (class != nil) {
        NSLog(@"Found one! %@", string);
    }
}];


Enumerator.m

@implementation Enumerator

+ (void)enumerateAllStringCombinationsInSet:(NSString*)set toMaximumLength:(NSInteger)maximumLength withBlock:(void(^)(NSString *string))actionBlock {

    const char *charSet = [set UTF8String];

    NSInteger i, j, k;
    NSInteger setLength = [set length];
    NSInteger lastPlace = setLength-1;
    NSInteger activePlace = 0;

    NSInteger *places = calloc(maximumLength, sizeof(NSInteger));

    char lastChar = charSet[lastPlace];
    char *cStrBuffer = calloc(maximumLength, sizeof(char));


    BOOL go = true;

    while (go) {
        // increment active place
        for (j=0; j&lt;setLength; ++j) {
            cStrBuffer[activePlace] = charSet[j];
            places[activePlace] = j;
            actionBlock([NSString stringWithUTF8String:cStrBuffer]);
        }

        for (i=activePlace; i &gt;= 0; --i) {
            // check for carry over / overflow
            if (cStrBuffer[i] == lastChar) {
                // carry over, set to "zero" and defer processing to one place to the left.
                if (i == 0) {
                    if (++activePlace &gt;= maximumLength) {
                        //  maximum size has been reached because all places are maxed out
                        go = false;
                        break;
                    }
                }

                cStrBuffer[i] = charSet[0];
                places[i] = 0;

                // otherwise, check previous char
            } else {

                // safe to increment this char up
                cStrBuffer[i] = charSet[places[i]+1];
                places[i] = places[i]+1;

                if (i&lt;activePlace) {
                    // set all trailing figures to "zero"
                    for (k = i+1; k &lt; activePlace; ++k) {
                        cStrBuffer[k] = charSet[0];
                        places[k] = 0;
                    }
                }
                break;
            }
        }
    }

    NSLog(@"Done!");
}

@end


Output:

Started at 3:07AM




2015-10-05 03:07:38.572 ClassFinder[59704:4595935] Here we go!
2015-10-05 03:09:29.770 ClassFinder[59704:4595935] Found one! NSSet
2015-10-05 03:09:29.774 ClassFinder[59704:4595935] Found one! NSURL
2015-10-05 03:10:27.769 ClassFinder[59704:4595935] Found one! UINib
2015-10-05 03:26:32.810 ClassFinder[59704:4595935] Found one! ALCity



It's now 3:29AM and it has made it all the way to the string AcAeZP.
First and foremost, I must absolutely recommend against doing anything of this sort.  

If you want to know what classes are available in the scope, you need only look up at the imports you're making.  All of the imports will either be your own files, for which you should only really have one visible class per import, or they will be library/framework imports.  The library/framework imports are likely bringing multiple classes with them, but they should be well documented.

Anything that a program like yours does manage to find is going to fall into one of two categories:


Stuff we already know about anyway.
Stuff that's undocumented and we almost certainly shouldn't be using.


And perhaps more importantly, this is incredibly inefficient.  The difference between just looking at your source code and making this run-time check is, well, your check is happening at run-time.  But, what useful thing can you actually do with that knowledge?  None, really.

More importantly, when do you know to stop looking?  There's not a character limit to class names (as far as I know).  All we can do is check literally every character combination.  The math to figure out how many combinations we need to check is:

Number of characters = N
Maximum length = M
Minimum length = m


  (N^M) - (N^(m-1))


(roughly... I might be slightly off)

If we don't impose a minimum length, and we just want to check every combination with 8 characters, we have to check 248,155,780,267,521 combinations.  That's 248 trillion, and that's just for every combination of characters up to 8 length, so forget about discovering anything that starts with NSMutable which alone is already 9 characters long before we count the last part (Array, Dictionary, Set, Data, String, etc).

If I do 63^255 on the OS X calculator, it says simply "Infinity".  While that's not technically correct, it might as well be.  And I don't even know that 255 characters is actually any sort of limit on class name length.



But, with all of this said, a little multithreading can go quite a long ways.

With two threads working on this problem, you're checking twice as many strings per cycle.  With four, four times as many, etc.  All that is required is a strategy for effectively breaking your total workload into equal chunks of work for all of the threads to work on.

And it doesn't matter if you create more threads than your CPU can actively work on.  The overhead for creating threads is relatively low.  

What might make sense is to create 53 separate threads--one for each of the valid starting characters (class names can not start with numbers).  It's unlikely we'll be running this on a CPU that can simultaneously process 53 threads at a time any time soon, so this should be more than plenty to make sure we're getting maximum value out of our CPU.