157

I need to hide (make private) the -init method of my class in Objective-C.

How can I do that?

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
lajos
  • 25,525
  • 19
  • 65
  • 75
  • 3
    There is now a specific, clean, and descriptive facility to achieve this, as shown in [this answer below](http://stackoverflow.com/a/27693034/2547229). Specifically: `NS_UNAVAILABLE`. I would generally urge you to use this approach. Would the OP consider revising their accepted answer? The other answers here provide a lot of useful detail, but are not the preferred method of achieving this. – Benjohn Dec 20 '16 at 08:01
  • As others have noted below, `NS_UNAVAILABLE` still allows a caller to invoke `init` indirectly via `new`. Simply overriding `init` to return `nil` will handle both cases. – Greg Brown Jan 10 '17 at 14:13
  • you could, of course make 'new' NS_UNAVAILABLE as well as 'init' - which is today a common practice. – Motti Shneor Jul 27 '20 at 18:28

9 Answers9

367

NS_UNAVAILABLE

- (instancetype)init NS_UNAVAILABLE;

This is a the short version of the unavailable attribute. It first appeared in macOS 10.7 and iOS 5. It is defined in NSObjCRuntime.h as #define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE.

There is a version that disables the method only for Swift clients, not for ObjC code:

- (instancetype)init NS_SWIFT_UNAVAILABLE;

unavailable

Add the unavailable attribute to the header to generate a compiler error on any call to init.

-(instancetype) init __attribute__((unavailable("init not available")));  

compile time error

If you don't have a reason, just type __attribute__((unavailable)), or even __unavailable:

-(instancetype) __unavailable init;  

doesNotRecognizeSelector:

Use doesNotRecognizeSelector: to raise a NSInvalidArgumentException. “The runtime system invokes this method whenever an object receives an aSelector message it can’t respond to or forward.”

- (instancetype) init {
    [self release];
    [super doesNotRecognizeSelector:_cmd];
    return nil;
}

NSAssert

Use NSAssert to throw NSInternalInconsistencyException and show a message:

- (instancetype) init {
    [self release];
    NSAssert(false,@"unavailable, use initWithBlah: instead");
    return nil;
}

raise:format:

Use raise:format: to throw your own exception:

- (instancetype) init {
    [self release];
    [NSException raise:NSGenericException 
                format:@"Disabled. Use +[[%@ alloc] %@] instead",
                       NSStringFromClass([self class]),
                       NSStringFromSelector(@selector(initWithStateDictionary:))];
    return nil;
}

[self release] is needed because the object was already allocated. When using ARC the compiler will call it for you. In any case, not something to worry when you are about to intentionally stop execution.

objc_designated_initializer

In case you intend to disable init to force the use of a designated initializer, there is an attribute for that:

-(instancetype)myOwnInit NS_DESIGNATED_INITIALIZER;

This generates a warning unless any other initializer method calls myOwnInit internally. Details will be published in Adopting Modern Objective-C after next Xcode release (I guess).

Jano
  • 62,815
  • 21
  • 164
  • 192
  • This can be good for methods other than `init`. Since, if this method is invalid, why do you initialize an object? Plus, when throwing an exception you will be able to specify some custom message communicating the correct `init*` method to the developer, while you don't have such option in case of `doesNotRecognizeSelector`. – Aleks N. Aug 27 '12 at 11:31
  • No idea Aleks, that shouldn't be there :) I edited the answer. – Jano Aug 28 '12 at 01:19
  • This is weird because it ended up crashing your system. I guess it is better to not let something happen but I am wondering if there is a better way. What I want is for other developers to not be able to call it and let it be caught up by the compiler when 'running' or 'building' – oky_sabeni Apr 19 '13 at 17:36
  • Actually, @Jano the unavailable attribute gives me an error. It says expected method body. Do you have more resources that you can point me to? Thanks. – oky_sabeni Apr 19 '13 at 20:06
  • 1
    I tried this and it doesn't work: - (id) init __attribute__((unavailable("init not available"))) { NSAssert(false,@"Use initWithType"); return nil; } – oky_sabeni Apr 19 '13 at 20:15
  • @Yko: It is just for header. There shouldn't be any body. Here is an example use with a singleton: http://stackoverflow.com/a/16103309/412916 See if that works in your environment. – Jano Apr 20 '13 at 00:34
  • Calling [Type new] – Matt Wielbut Oct 19 '13 at 10:05
  • @mwielbut You could add attribute unavailable for new too. If you want text formatted as code in SO surround it with backticks. – Jano Oct 19 '13 at 11:28
  • "Add the unavailable attribute to the header to generate a compiler error on any call to init." @Jano - Unfortunately, this isn't entirely true. Marking init as unavailable will result in a compiler warning with the code `[[MyClass alloc] init]` but `[MyClass new]` will be unaffected and will happily call the "unavailable" init method. – Vince O'Sullivan May 07 '14 at 11:35
  • @VinceO'Sullivan the compiler doesn't flag the call to `-init` from `+new` because the later is linked (not compiled) from the runtime at `/usr/lib/libobjc.dylib`. If you intend to discourage instance creation mark `+new` as unavailable, but note that the developer will still be able to create instances using runtime functions. – Jano May 07 '14 at 12:29
  • I tried this: -(instancetype)myOwnInit NS_DESIGNATED_INITIALIZER; but it is giving compilation error: Expected ';' after message prototype, any ideas? – Devarshi Jun 09 '14 at 11:52
  • 1
    @Miraaj Sounds like it's not supported on your compiler. It is supported in Xcode 6. You should get “Convenience initializer missing a 'self' call to another initializer” if an initializer doesn't call the designated one. – Jano Jun 09 '14 at 19:04
  • I would like to hide `init` for singleton. And the first method in your answer seems most elegant. However, with that I won't even be able to init my instance in the `sharedWhatever` method. Any idea how to resolve this? – Yuchen Nov 07 '14 at 15:32
  • Xcode has the NS_UNAVAILABLE; macro which you can use as well. e.g. `-(instancetype)init NS_UNAVAILABLE;`, which is a bit nicer than using the attributes directly, and potentially safer – Orion Edwards Dec 16 '14 at 20:22
  • 1
    Xcode 12: `NS_SWIFT_UNAVAILABLE` lead to compile error, `__unavailable` worked. – kelin Sep 05 '22 at 14:31
101

Apple has started using the following in their header files to disable the init constructor:

- (instancetype)init NS_UNAVAILABLE;

This correctly displays as a compiler error in Xcode. Specifically, this is set in several of their HealthKit header files (HKUnit is one of them).

lehn0058
  • 19,977
  • 15
  • 69
  • 109
90

Objective-C, like Smalltalk, has no concept of "private" versus "public" methods. Any message can be sent to any object at any time.

What you can do is throw an NSInternalInconsistencyException if your -init method is invoked:

- (id)init {
    [self release];
    @throw [NSException exceptionWithName:NSInternalInconsistencyException
                                   reason:@"-init is not a valid initializer for the class Foo"
                                 userInfo:nil];
    return nil;
}

The other alternative — which is probably far better in practice — is to make -init do something sensible for your class if at all possible.

If you're trying to do this because you're trying to "ensure" a singleton object is used, don't bother. Specifically, don't bother with the "override +allocWithZone:, -init, -retain, -release" method of creating singletons. It's virtually always unnecessary and is just adding complication for no real significant advantage.

Instead, just write your code such that your +sharedWhatever method is how you access a singleton, and document that as the way to get the singleton instance in your header. That should be all you need in the vast majority of cases.

jlehr
  • 15,557
  • 5
  • 43
  • 45
Chris Hanson
  • 54,380
  • 8
  • 73
  • 102
  • 2
    Is the return actually necessary here? – philsquared Feb 16 '09 at 13:51
  • 5
    Yes, to keep the compiler happy. Otherwise the compiler may complain that there's no return from a method with non-void return. – Chris Hanson Feb 16 '09 at 23:27
  • Funny, it doesn't for me. Perhaps a different compiler version or switches? (I'm just using the default gcc switches with XCode 3.1) – philsquared Feb 17 '09 at 13:37
  • This isn't just useful for singletons, but also for objects that will deallocate themselves when not needed. – Casebash Jan 29 '10 at 02:03
  • @Casebash, sometimes the warnings only show when you've done a clean first. – Dan Rosenstark Aug 18 '11 at 15:59
  • 3
    Counting on the developer to follow a pattern isn't a good idea. It's better to throw an exception, so developers in a different team knows not to. I private concept would be better. – Nick Turner May 03 '13 at 15:14
  • For singletons, I prefer to override `+allocWithZone:` to call `[[super allocWithZone:zone] initSingleton]` the first time it's called, use an empty public `-init` method, and have a `+sharedWhatever` method that calls `+allocWithZone:`. This approach is as compatible as possible with IB. It's definitely a matter of taste, though. – Becca Royal-Gordon May 12 '13 at 03:23
  • 4
    *"For no real significant advantage"*. Completely untrue. The significant advantage is that you want to **enforce** the singleton pattern. If you allow new instances to be created, then a developer who's not familiar with the API may use `alloc` and `init` and have their code function incorrectly because they have the right class, but the wrong instance. This is the essence of the principle of *encapsulation* in OO. You hide things in your APIs that other classes shouldn't need, or get, access to. You don't just keep everything public and expect humans to keep track of it all. – Nate Mar 31 '14 at 21:57
  • Part of the problem is that Singleton is generally a bad pattern, and often unnecessary. While occasionally there's a reason to have a "shared whatever" or "whatever manager," you often don't need to **enforce** that because the natural coding style will be to use `+sharedWhatever` to get at it, and the lack of a documented `-init` for it will be enough to be to keep people from instantiating one separately. In the meantime, you haven't tied your hands **against** creating one separately in things like unit tests, or in future versions of your software. – Chris Hanson Apr 03 '14 at 19:28
  • cannnot call self release anymore ARC forbids it – Bhargav Aug 10 '16 at 10:59
  • @Bhargav you can disable ARC for a specific file, if needed. – Otávio Sep 20 '16 at 06:52
4

You can declare any method to be not available using NS_UNAVAILABLE.

So you can put these lines below your @interface

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

Even better define a macro in your prefix header

#define NO_INIT \
- (instancetype)init NS_UNAVAILABLE; \
+ (instancetype)new NS_UNAVAILABLE;

and

@interface YourClass : NSObject
NO_INIT

// Your properties and messages

@end
Kaunteya
  • 3,107
  • 1
  • 35
  • 66
3

That depends on what you mean by "make private". In Objective-C, calling a method on an object might better be described as sending a message to that object. There's nothing in the language that prohibits a client from calling any given method on an object; the best you can do is not declare the method in the header file. If a client nevertheless calls the "private" method with the right signature, it will still execute at runtime.

That said, the most common way to create a private method in Objective-C is to create a Category in the implementation file, and declare all of the "hidden" methods in there. Remember that this won't truly prevent calls to init from running, but the compiler will spit out warnings if anyone tries to do this.

MyClass.m

@interface MyClass (PrivateMethods)
- (NSString*) init;
@end

@implementation MyClass

- (NSString*) init
{
    // code...
}

@end

There's a decent thread on MacRumors.com about this topic.

Matt Dillard
  • 14,677
  • 7
  • 51
  • 61
  • 3
    Unfortunately, in this case, the category approach won't really help. Normally it buys you compile-time warnings that the method may not be defined on the class. However, since MyClass must inherit from one of the root clases and they define init, there will be no warning. – Barry Wark Nov 14 '08 at 16:04
3

If you are talking about the default -init method then you can't. It's inherited from NSObject and every class will respond to it with no warnings.

You could create a new method, say -initMyClass, and put it in a private category like Matt suggests. Then define the default -init method to either raise an exception if it's called or (better) call your private -initMyClass with some default values.

One of the main reasons people seem to want to hide init is for singleton objects. If that's the case then you don't need to hide -init, just return the singleton object instead (or create it if it doesn't exist yet).

Nathan Kinsinger
  • 23,641
  • 2
  • 30
  • 19
  • This seems like a better approach than just leaving 'init' alone in your singleton, and relying upon documentation to communicate to the user that they're supposed to access via 'sharedWhatever'. People typically don't read docs until they've already wasted many minutes trying to figure out an issue. – Greg Maletic Jun 29 '11 at 18:23
3

Put this in header file

- (id)init UNAVAILABLE_ATTRIBUTE;
Jerry Juang
  • 147
  • 2
  • 2
  • This is not recommended. Apple's modern objective-c documents state that init should return instancetype, not id. https://developer.apple.com/library/ios/releasenotes/ObjectiveC/ModernizationObjC/AdoptingModernObjective-C/AdoptingModernObjective-C.html – lehn0058 Sep 09 '15 at 14:24
  • 5
    and it's: - (instancetype) init NS_UNAVAILABLE; – bandejapaisa Oct 08 '15 at 10:51
2

well the problem why you can't make it "private/invisible" is cause the init method gets send to id (as alloc returns an id) not to YourClass

Note that from the point of the compiler (checker) an id could potencialy respond to anything ever typed (it can't check what really goes into the id at runtime), so you could hide init only when nothing nowhere would (publicly = in header) use a method init, than the compile would know, that there is no way for id to respond to init, since there is no init anywhere (in your source, all libs etc...)

so you cannot forbid the user to pass init and get smashed by the compiler... but what you can do, is to prevent the user from getting a real instance by calling a init

simply by implementing init, which returns nil and have an (private / invisible) initializer which name somebody else won't get (like initOnce, initWithSpecial ...)

static SomeClass * SInstance = nil;

- (id)init
{
    // possibly throw smth. here
    return nil;
}

- (id)initOnce
{
    self = [super init];
    if (self) {
        return self;
    }
    return nil;
}

+ (SomeClass *) shared 
{
    if (nil == SInstance) {
        SInstance = [[SomeClass alloc] initOnce];
    }
    return SInstance;
}

Note : that somebody could do this

SomeClass * c = [[SomeClass alloc] initOnce];

and it would in fact return a new instance, but if the initOnce would nowhere in our project be publicly (in header) declared, it would generate a warning (id might not respond ...) and anyway the person using this, would need to know exactly that the real initializer is the initOnce

we could prevent this even further, but there is no need

Peter Lapisu
  • 19,915
  • 16
  • 123
  • 179
0

I have to mention that placing assertions and raising exceptions to hide methods in the subclass has a nasty trap for the well-intended.

I would recommend using __unavailable as Jano explained for his first example.

Methods can be overridden in subclasses. This means that if a method in the superclass uses a method that just raises an exception in the subclass, it probably won't work as intended. In other words, you've just broken what used to work. This is true with initialization methods as well. Here is an example of such rather common implementation:

- (SuperClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
    ...bla bla...
    return self;
}

- (SuperClass *)initWithLessParameters:(Type1 *)arg1
{
    self = [self initWithParameters:arg1 optional:DEFAULT_ARG2];
    return self;
}

Imagine what happens to -initWithLessParameters, if I do this in the subclass:

- (SubClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
    [self release];
    [super doesNotRecognizeSelector:_cmd];
    return nil;
}

This implies that you should tend to use private (hidden) methods, especially in initialization methods, unless you plan to have the methods overridden. But, this is another topic, since you don't always have full control in the implementation of the superclass. (This makes me question the use of __attribute((objc_designated_initializer)) as bad practice, although I haven't used it in depth.)

It also implies that you can use assertions and exceptions in methods that must be overridden in subclasses. (The "abstract" methods as in Creating an abstract class in Objective-C )

And, don't forget about the +new class method.

Community
  • 1
  • 1
techniao
  • 151
  • 1
  • 9