0

Below there are two methods to programmatically alloc and init objects of various classes and 'types'.

- (id)buildObjectOfClass:(NSString *)classString andType:(NSString *)typeString
    {
    id buildObject;
    Class className             = NSClassFromString(classString);
    SEL initWithTypeSelector    = NSSelectorFromString(@"initWithType:");

    if ([className instancesRespondToSelector:initWithTypeSelector] == YES) {
        buildObject = [[className alloc] performSelector:initWithTypeSelector 
                                              withObject: typeString];
    }
    return buildObject;
}

This method implementation was originally written more tersely as simply:
{ return [[className alloc] initWithType:typeString]; }

My questions are: 1) is the verbose version necessary? and 2) if so, was it programmed as best as it could be? Are there shortcuts or best practices I am neglecting?

user2000809
  • 496
  • 2
  • 13
  • 1
    Can you explain the *why* of this? I can't think of a single instance where I would ever want to do this. You're opening yourself up to a lot of errors/crashes by building classes and selectors from strings. – Paul Armstrong May 14 '13 at 16:30

1 Answers1

2

The difference between the verbose and terse versions of this method are that the verbose version validates that the class instances can actually respond to -initWithType: which is not a standard NSObject init function.

It is unnecessary to use the verbose version if any of the following were true:

  • You are only using -init and not -initWithType:
  • You are certain that every class you instantiate will be able to handle -initWithType:
  • You don't mind the application unexpectedly quitting with an unknown method exception if the class you instantiate does not respond to -initWithType:

This version (although you should set buildObject to nil to handle the error case explicitly) returns nil if the class isn't found or if it doesn't respond to -initWithType:. The terse version returns nil if the class isn't found and throws an exception if the class instances don't respond to -initWithType:.

gaige
  • 17,263
  • 6
  • 57
  • 68
  • The long version returns `nil` when the `respondsToSelector:` check fails _only_ when compiling under ARC. Otherwise it returns garbage. – jscs May 14 '13 at 18:25
  • @JoshCaswell I believe I made a specific reference to that fact at the start of my final paragraph. – gaige May 14 '13 at 18:48
  • I was replying directly to that. That sentence reads to me as if you're saying that `buildObject` will always be `nil` if the instance does not respond to `initWithType:`. That's not true if ARC is not enabled. – jscs May 14 '13 at 18:51
  • @JoshCaswell Specifically: `although you should set buildObject to nil to handle the error case explicitly` is what I'm talking about. It's true that I don't specifically call out ARC vs non-ARC, but it's bad form regardless in any C-variant language to return an uninitialized value. – gaige May 14 '13 at 18:52
  • Yes, we agree about that practice. What I'm saying is that your statement «This version ... returns `nil` if the class isn't found or if it doesn't respond to `-initWithType:`.» is _not correct_ without including ARC as a condition. ARC initializes object variables to `nil`; without it they are like any other stack variable and point to junk. And this is exactly _why_ it should be initialized manually. – jscs May 14 '13 at 18:59