views:

165

answers:

4

I want to create a controller that depends on the class of a given instance of a model

 -(BaseController *)getControllerForModel:(Model *)model
    {
     BaseController *controller = nil;
     Class controllerClass = [BaseController class]; //the default value

        //find the right controller
     if ([model isMemberOfClass:[ModelClass1 class]]) 
      controllerClass = [ConcreteController1 class];
     else if ([model isMemberOfClass:[ModelClass2 class]])
      controllerClass = [ConcreteController2 class];
     else if ([model isMemberOfClass:[ModelClass3 class]])
      controllerClass = [ConcreteController3 class];
...
     else if ([model isMemberOfClass:[ModelClassX class]])
      controllerClass = [ConcreteControllerX class];
     else
      Trace(TRACELEVEL_WARNING, @"Unrecognized model type: %@", NSStringFromClass([model class]));

     //Now instantiate it with the model
     controller = [[[controllerClass alloc] initWithModel:model] autorelease];
     return slotController;
    }

I want to find a more flexible solution to this and thought of having a dictionary, which maps Model-Classes to Controller-Classes and then NSClassFromString could give me the right instance.

My question is this: Is NSClassFromString using much of my applications performance if i use it several times (say, 100 times at once)? Or would it be about as fast as the above approach?

+3  A: 

Generally, use of isMemberOfClass: in such a fashion indicates an architectural issue.

In this case, why can't the various Model classes simply implement a +controllerClass method?

I can understand Nikolai's desire to maintain the layering of Controller on top of Model without the Model having knowledge of the Controller. However, my experience is that any control layer that has such type specific knowledge of the Model quickly destroys that isolation anyway; the model layer quickly evolves behaviors that are controller specific.

Thus, the added complexity injected by attempting to maintain that separation just isn't worth it.

bbum
Keeping the controller layer out of the model seems right to me.
Nikolai Ruhe
Jumping through hoops to do so really doesn't maintain the isolation and creates unnecessary complexity. As well, such a design really isn't isolating controller form model anyway, given that the controllers are so tightly intertwined with the models.
bbum
But it's isolating the model from the controller layer, which is often a good thing. Especially when the model layer is designed for reuse.
Nikolai Ruhe
This might seem like its missing the point, but if you encapsulated the +controllerClass method in a category defined in the same file as the specific controller class, wouldn't that separate the two enough? (Like apple does with NSString methods for drawing being defined in UIKit)
Jared P
while i like this approach very much, it would make it more complex to change a range of controllers for the appropriate model classes (which is a case that might really happen). It also binds the controller to the model, which i really dont want.In my case, i take some more complexity and allow more flexibility in the configuration of the app, since it has to be very reusable.I will keep this solution in mind, since it would allow a very simple implementation, but i don't think it will work with my architecture
Tomen
@Jared P: That's definitely not missing the point. This proposal keeps controller selection and implementation together. I like it.
Nikolai Ruhe
Alternatively, stick the table of gunk in a plist, load it, and turn it into a hash of name -> class or class -> class, like drawonward said below.
bbum
+2  A: 

If you're really concerned about performance, you could cache the results of the NSClassFromString and put them in a dictionary.

On the other hand, that's probably pretty much what NSClassFromString really does (a map lookup). So I'd say it's much faster that the 100x if approach.

But anyway: Just give it a try, as with all performance issues.

Nikolai Ruhe
I think NSClassFromString also acquires a runtime lock, slowing down other threads/spinning.
Jared P
+1  A: 

NSStringFromClass shouldn't be much more expensive. In fact, in your case you could use it much more effectively. I don't know your exact class naming convention, but using a methof like this could be much smaller and faster:

NSString * modelClassName = NSStringFromClass([model class]);

// get the suffix of class name
// for example, '3' in 'ModelClass3'
NSString * suffix = [modelClassName substringFromIndex:10];

// create the controller name
NSString * controllerName = [NSString stringWithFormat:@"ConcreteController%@", suffix];

/* do something if name is not valid */
controllerClass = NSClassFromString(controllerName);

as bbum suggested you could also make this a method on a common parent of the model classes.

Mo
+3  A: 

A Class is an id, and as such can be added to an NSDictionary. You should try the following:

mModelToControllerMap = [[NSDictionary alloc] initWithObjectsAndKeys:
   [ConcreteController1 class] , [ModelClass1 class] ,
   [ConcreteController2 class] , [ModelClass2 class] ,
   ...
   nil];

Then later:

controllerClass = [mModelToControllerMap objectForKey:[modelInstance class]];
if ( controllerClass ) ...
else ...

If you make it a mutable dictionary, you can then have controllers register for the models they want instead of forcing the base class to be aware of all derived classes.

And a direct answer. At worst, NSClassFromString may need to iterate through all classes to find a match. It could also be a dictionary lookup, but the implementation is not documented and should not be relied on. While doing it a few hundred times probably isn't too bad, there are better ways.

drawnonward