Objective-C: new is dangerous (and old), avoid it at all costs

Using the new factory method might seem appealing: you no longer have to type [[MyClass alloc] init], you no longer need to write custom factory methods to your class, in short less code. And this is not a bad thing, less code to write means less code to maintenance, test, etc.

new unfortunately has a big flaw, mainly caused by the fact that new translates to alloc+init. This makes it easy to mistakenly use init where you should not be allowed to. One situation that falls into this category is when init is declared with the NS_UNAVAILABLE attribute, which causes compile errors when used directly.

Let’s consider a short example:

@interface MyPerson: NSObject
@property (nonatomic, readonly, strong, nonnull) NSString *name;
- (nonnull instancetype)init NS_UNAVAILABLE;
- (nonnull instancetype)initWithName:(nonnull NSString *)name NS_DESIGNATED_INITIALIZER;
Code language: Objective-C (objectivec)

We try to be very clear and specific regarding the class: it should have a non-nil name, and you should be able to instantiate it only via the initWithName: intializer. The basic init would not make sense here, since we’d end up with an invalid instance (name would be nil), so we mark it as unavailable.

Trying to write [[MyPerson alloc] init] results in a clear compile error: 'init' is unavailable. On the other hand [MyPerson new] successfully compiles and silently fails at runtime as we end up with an invalid instance. This can result in some bugs hard to find, especially if new is used only in some places of the application.

And even if new makes sense at the time the class is written – i.e. init gives a valid instance, things can change in the future if other developer (or even yourself) change the class initializers and make init unavailable, at which point things might start to go wild if some of the new calls are missed and not replaced by the new ones. On the other hand alloc init doesn’t suffer from this problem, as you’ll instantly get compile errors all over the place where the construct is used.

Swift handles this a little bit better by crashing the app if new is used on a class that has init exported as unavailable – for example if you add a required parametrized initializer to the class. The code still compiles, but you no longer get a silent failure, and the app simply crashes. This helps as it gives you the exact root cause of the problem, but, depending on the application, might not be a preferable solution.

alloc init gives you bug free code courtesy of the compiler. new leads to error prone and less forward compatible code. Let’s stop using it.

Leave a Reply

Your email address will not be published. Required fields are marked *