[ACCEPTED]-Is it possible to make the -init method private in Objective-C?-objective-c

Accepted answer
Score: 364

NS_UNAVAILABLE

- (instancetype)init NS_UNAVAILABLE;

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

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

- (instancetype)init NS_SWIFT_UNAVAILABLE;

unavailable

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

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

compile time error

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

-(instancetype) __unavailable init;  

doesNotRecognizeSelector:

Use doesNotRecognizeSelector: to raise 13 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 12 NSInternalInconsistencyException and show 11 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 10 needed because the object was already allocated. When 9 using ARC the compiler will call it for 8 you. In any case, not something to worry 7 when you are about to intentionally stop 6 execution.

objc_designated_initializer

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

-(instancetype)myOwnInit NS_DESIGNATED_INITIALIZER;

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

Score: 101

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

- (instancetype)init NS_UNAVAILABLE;

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

Score: 89

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

What you can do is throw an NSInternalInconsistencyException if your 16 -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 15 — which is probably far better in practice 14 — is to make -init do something sensible for 13 your class if at all possible.

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

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

Score: 4

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

So you can put these lines below 2 your @interface

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

Even better define a macro 1 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
Score: 3

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

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

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

Score: 3

Put this in header file

- (id)init UNAVAILABLE_ATTRIBUTE;

0

Score: 2

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

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

MyClass.m

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

@implementation MyClass

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

@end

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

Score: 2

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

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

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

simply by implementing init, which 12 returns nil and have an (private / invisible) initializer 11 which name somebody else won't get (like 10 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 9 somebody could do this

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

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

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

Score: 0

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

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

Methods can be 22 overridden in subclasses. This means that 21 if a method in the superclass uses a method 20 that just raises an exception in the subclass, it 19 probably won't work as intended. In other 18 words, you've just broken what used to work. This 17 is true with initialization methods as well. Here 16 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 15 what happens to -initWithLessParameters, if 14 I do this in the subclass:

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

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

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

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

More Related questions