Is it possible to make the -init method private in Objective-C?
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
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.
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).
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")));
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 alloc
ated. 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).