views:

613

answers:

5

Example:

I have 10 view controllers, which are all allocated and initialized in the same way:

UIViewController *controller = [[MyViewController alloc] initWithNib];

(note that -initWithNib is a custom method of a UIViewController subclass)

The next view controller class is OtherViewController, and so on. I want to load the view controllers lazily, just when I need them. But to do that, I need to have some kind of "array" that will give me the corresponding class for a given index, so that I can initialize it.

I ended up creating a method with a big switch-statement, that will just do that nasty allocation and initialization separately for every single view controller. I'm not happy with that. There it would be much better if I could assign the appropriate class to a variable, and then at the end of the switch statement just allocate and initialize that class from the variable.

Is there a way to achieve that?

EDIT: I've found a function

id class_createInstance(Class cls, size_t extraBytes)

and every class seems to have a property "class". But I can't assign it to an instance variable. This doesn't work:

Class cls = [UIImage class];
cls *image = [cls imageNamed:@"avatar.png"];

The first line compiles. But the second one gives an error: "image undeclared".

A: 

What you want is Reflection. No idea of objective-c has it though - but the term might help you Google for your answer better.

Gandalf
-1??? Give me a break - it's a completely correct answer and was also the only answer initially after an hour. Just because you don't know a specific language doesn't mean an answer is incorrect - it very well could have helped another user to answer the question. Some of the people on this site need to take themselves less seriously, or find a new hobby.
Gandalf
There's always people who are going to vote down any answer they see as unfit for any reason. You get far more credit for an upvote than a downvote costs you. Just don't let it bother you, it's bad for the blood pressure.
mmc
+3  A: 

You want to use reflection:

 id controller = class_createInstance(NSClassFromString(@"your class name"), 0/*extra bytes*/);

Objective-C Runtime Reference

arul
I'm getting a warning: "implicit declaration of function 'class_createInstance'"
Thanks
#import <objc/objc-runtime.h>
Daniel Dickison
@arul, is it possible to do [[NSClassFromString(@"MyClass") alloc] init]?
Daniel Dickison
@Daniel: yes it is.
arul
A: 

The Objective C Reference also will be a good place to look to get the calls. Try searching for Objective-C 2.0 Runtime Reference since I cant add links

+5  A: 

If you know the names of the classes at compile time, you can assign the classes to Class variables. For example:

static Class factory[2];

factory[0] = [MyViewController1 class];
factory[1] = [MyViewController2 class];
...

Then you could have (classid would be a constant known at compile time that would map to a desired class:

-(UIViewController*)createViewController:(int)classid
{
    return [[factory[classid] alloc] init];
}

Assuming that method is defined in a class named MyFactory, you can then do:

MyFactory * fac = [[MyFactory alloc] init];
UIViewController * v1 = [fac createViewController: 0]; // typed
id v2 = [fac createViewController: 1]; // untyped

If you don't have the compile time name of the class, you can simply do the following:

#include <objc/objc-runtime.h>

id object = [[NSClassFromString(@"TheClassName") alloc] init];

Since your original question involves a set of UIViewControllers though, there's no reason to lose type safety with the latter method.

Jason
A: 

I blogged about this last month at: http://igotosoft.blogspot.com/2009/05/dynamically-creating-viewscontrollers.html Essentially, it involves a new class I call the ClassConstructor, which takes a class name, init method name, and comma separated arguments. When you need to create an instance of that class, just use your [myClassConstructor create];