42

When designing a class hierarchy, sometimes the subclass has added a new initWithSomeNewParam method, and it would be desirable to disable calls to the old init method inherited from the superclass.

First of all, I've read the question here, where the proposed alternatives are either override init to throw an exception at runtime, or override and set default values for properties. In my case, I don't wan't to provide default values, and I want to clearly indicate that the old method should not be called, and instead the new method with parameters should be used.

So the runtime exception are fine, but unless the code is debugged, there's no way for other programmers in the team to notice that the old method is no longer intended to be used.

If I'm correct, there's no way to mark a method as "private". So, apart from adding comments, is there a way to do this?

Thanks in advance.

Community
  • 1
  • 1
Mister Smith
  • 27,417
  • 21
  • 110
  • 193

5 Answers5

108

You can explicitly mark your init as being unavailable in your header file:

- (id) init __unavailable;

or:

- (id) init __attribute__((unavailable));

With the later syntax, you can even give a reason:

- (id) init __attribute__((unavailable("Must use initWithFoo: instead.")));

The compiler then issues an error (not a warning) if someone tries to call it.

DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • 2
    very nice, didn't know about that too. – Leonardo Baptista Mar 09 '12 at 13:13
  • This does not affect the `[MyClass new]` syntax. Such a call will still use the init method, without any error or warning. – Vince O'Sullivan May 07 '14 at 11:42
  • 2
    @VinceO'Sullivan: I consider the use of `new` to be bad style and I know no one who's using it, but if you do: just mark `new` on that class with the described attribute as well. – DarkDust May 07 '14 at 13:04
  • @DarkDust: What is your basis for regarding `[MyClass new]` to be bad style? – Vince O'Sullivan May 07 '14 at 14:00
  • @VinceO'Sullivan: For a discussion, see [this question](http://stackoverflow.com/questions/719877/use-of-alloc-init-instead-of-new). Basically, I think it's a wart. It's a left-over from NeXTSTEP days. It's explicit vs. implicit. Use of `new` sometimes and for everything else `initWithFoo:` breaks consistency. Imagine someone overrides `new` and calls a different initializer than `init`: totally unexpected behavior. So it's mostly a matter of "taste", though, few hard technical reasons. – DarkDust May 07 '14 at 14:43
  • Also, you can just do: `+ (id) new __attribute__((unavailable("Must use otherInitializer: instead.")));` – Sam Nov 05 '14 at 14:48
  • 1
    If we use this the compiler would not allow us to init the object in its own class methods as well. this would cause problem while implementing singleton pattern. is there a solution to this problem ? – sarfarazsajjad Aug 04 '15 at 09:08
  • 1
    @Saifee: Simply chose a different name for your internal initializer for singleton patterns, like `initInternal` (Calling `[super init]` still works, BTW). – DarkDust Aug 04 '15 at 09:14
4

To add to what @DarkDust posted, you could alternatively use UNAVAILABLE_ATTRIBUTE

- (id)init UNAVAILABLE_ATTRIBUTE;

This will throw an error when a user tries to call init on an instance of this class.

RileyE
  • 10,874
  • 13
  • 63
  • 106
  • This is a shorter & useful form of @DarkDust's answer and is essentially the same: `#define UNAVAILABLE_ATTRIBUTE __attribute__((unavailable))` – iphondroid Oct 01 '20 at 11:32
1

Flag it deprecated? Developers will be developers, you can't stop us all! ;-)

How do I flag a method as deprecated in Objective-C 2.0?

Community
  • 1
  • 1
Jake
  • 3,973
  • 24
  • 36
0

The syntax has been shortened to just:

- (instancetype)init NS_UNAVAILABLE;
Andrew Paul Simmons
  • 4,334
  • 3
  • 31
  • 39
0

initWith:Stuff and:OtherStuff should never be more than convenience constructors.

In that they effectively should call

self = [self init];

if(self)
{
    self.stuff = Stuff;
    self.other = OtherStuff;
}

so [object init] will always return an object in a predefined state, and [object initWithStuff:stuff] will return the object in the predefined state with stuff overridden.

Basically what I'm getting at is, its bad practice to discourage [object init] especially when someone subclasses your subclass in the future….

Tony Million
  • 4,296
  • 24
  • 24
  • I beg to differ. I think what the poster wants makes perfect sense (and I've done it as well, though with runtime assertions). Sometimes it simply makes no sense to allocate an "empty" object and no defaults can be given. Especially if the instance is considered to be immutable or if it's a class cluster. – DarkDust Mar 09 '12 at 13:02
  • Yes, I know it is bad practice, but for some classes default values don't make sense. – Mister Smith Mar 09 '12 at 13:03
  • If thats the case, then its a perfect reason to use +(class)classWithPredefinedStuff:(Stuff*)stuff; – Tony Million Mar 09 '12 at 13:03
  • I agree with Tony, regardless of it being useful to disable it is a bad practice and should be avoided. @DarkDust; You can also init an immutable array. – Jake Mar 09 '12 at 14:48
  • @not really Jake: Right, and what do you get? An empty array. But if you wanted to have stuff in it, then `init` doesn't work for you. While it may make sense for an array to be empty, that's not true for every class. – DarkDust Mar 09 '12 at 17:07
  • @DarkDust, sometimes what is right doesn't always make sense and vice versa. Coding is much like poetry that way ;-) – Jake Mar 12 '12 at 13:04
  • @TonyMillion I just think this is wrong. If you are using constructor injection and your class is supposed to be immutable then this is complicating things and breaking encapsulation. – tacos_tacos_tacos Jul 29 '15 at 00:35