Search code examples
objective-ciosobjective-c-runtimeinline-code

Create custom dynamic classes in objective-c


In my application i have an UIViewController that i uses a lot of UIAlertView to ask things to the user.

Because i need the response of each UIAlertView i have made my controller a delegate of UIAlertViewDelegate, this works fine but after 7 UIAlertView's i`m trying to find a better way to use delegates.

In java i know that i can create inline classes for a single purpose, like in this question: Java - inline class definition

What i want to know is: Is there a way to create a class to be delegate dynamically? to achieve something like this

id<UIAlertViewDelegate> myCustomClass = @class {
    my class code goes here
}

UIAlertView* alertView;
alertView = [[UIAlertView alloc] initWithTitle:@"Title"
                                       message:@"Message"
                                      delegate:myCustomClass
                             cancelButtonTitle:@"No"
                             otherButtonTitles:@"OK", @"Sure", @"Maybe", nil] ];    
[alertView show];

Solution

  • No - there are no 'inline classes' in Objective-C. With that said, you can create custom objects at run-time with objective-c, which is a little bit more involved, but I'd be willing to share some code to do what you are saying.

    Here is an example of that:

    NSObject+Subclass.h

    #import <objc/runtime.h>
    
    typedef struct selBlockPair { SEL aSEL; id (^__unsafe_unretained aBlock)(id, ...); } selBlockPair;
    #define NIL_PAIR ((struct selBlockPair) { 0, 0 })
    #define PAIR_LIST (struct selBlockPair [])
    #define BLOCK_CAST (id (^)(id, ...))
    
    @interface NSObject (subclass)
    
    +(Class) newSubclassNamed:(NSString *) name
                protocols:(Protocol **) protos
                     impls:(selBlockPair *) impls;
    
    @end
    

    NSObject+Subclass.m

    @implementation NSObject (subclass)
    
    +(Class) newSubclassNamed:(NSString *)name
                 protocols:(Protocol **)protos
                     impls:(selBlockPair *)impls
    {
        if (name == nil)
        {
            // basically create a random name
            name = [NSString stringWithFormat:@"%s_%i_%i", class_getName(self), arc4random(), arc4random()];
        }
    
        // allocated a new class as a subclass of self (so I could use this on a NSArray if I wanted)
        Class newClass = objc_allocateClassPair(self, [name UTF8String], 0);
    
        // add all of the protocols untill we hit null
        while (protos && *protos != NULL)
        {
            class_addProtocol(newClass, *protos);
            protos++;
        }
    
        // add all the impls till we hit null
        while (impls && impls->aSEL)
        {
            class_addMethod(newClass, impls->aSEL, imp_implementationWithBlock(impls->aBlock), "@@:*");
            impls++;
        }
    
        // register our class pair
        objc_registerClassPair(newClass);
    
        return newClass;
    }
    
    @end
    

    Example Usage:

    int main()
    {
        @autoreleasepool {
            __strong Class newClass = [NSString newSubclassNamed:@"MyCustomString" protocols:NULL impls: PAIR_LIST {
                @selector(description),
                BLOCK_CAST ^id (id self) {
                    return @"testing";
                },
                NIL_PAIR
            }];
    
            NSString *someString = [newClass new];
            NSLog(@"%@", someString);
        }
    }
    

    Output:

    2012-10-01 10:07:33.609 TestProj[54428:303] testing