是否可以在 Objective-C 中将 -init 方法设为私有?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/195078/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it possible to make the -init method private in Objective-C?
提问by lajos
I need to hide (make private) the -initmethod of my class in Objective-C.
我需要-init在 Objective-C 中隐藏(设为私有)我的类的方法。
How can I do that?
我怎样才能做到这一点?
采纳答案by Chris Hanson
Objective-C, like Smalltalk, has no concept of "private" versus "public" methods. Any message can be sent to any object at any time.
与 Smalltalk 一样,Objective-C 没有“私有”方法与“公共”方法的概念。任何消息都可以随时发送到任何对象。
What you can do is throw an NSInternalInconsistencyExceptionif your -initmethod is invoked:
NSInternalInconsistencyException如果您的-init方法被调用,您可以做的是抛出一个:
- (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 -initdo something sensible for your class if at all possible.
另一种选择——在实践中可能要好得多——是尽可能-init为你的班级做一些有意义的事情。
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.
如果您尝试这样做是因为您试图“确保”使用单例对象,请不要打扰。具体地,不与打扰“覆盖+allocWithZone:,-init,-retain,-release”创建单身的方法。它几乎总是不必要的,只是增加了复杂性而没有真正的显着优势。
Instead, just write your code such that your +sharedWhatevermethod 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.
相反,只需编写您的代码,以便您的+sharedWhatever方法是您访问单例的方式,并将其记录为在您的标头中获取单例实例的方式。在绝大多数情况下,这应该是您所需要的。
回答by Jano
NS_UNAVAILABLE
NS_UNAVAILABLE
- (instancetype)init NS_UNAVAILABLE;
This is a the short version of the unavailable attribute. It first appeared in macOS 10.7and iOS 5. It is defined in NSObjCRuntime.h as #define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE.
这是不可用属性的简短版本。它首先出现在 macOS 10.7和iOS 5 中。它在 NSObjCRuntime.h 中定义为#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE.
There is a version that disables the method only for Swift clients, not for ObjC code:
有一个版本仅对 Swift 客户端禁用该方法,而不对 ObjC 代码禁用:
- (instancetype)init NS_SWIFT_UNAVAILABLE;
unavailable
unavailable
Add the unavailableattribute to the header to generate a compiler erroron any call to init.
将该unavailable属性添加到标头以在对 init 的任何调用中生成编译器错误。
-(instancetype) init __attribute__((unavailable("init not available")));


If you don't have a reason, just type __attribute__((unavailable)), or even __unavailable:
如果您没有理由,只需键入__attribute__((unavailable)),甚至__unavailable:
-(instancetype) __unavailable init;
doesNotRecognizeSelector:
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.”
使用doesNotRecognizeSelector:提出一个NSInvalidArgumentException。“每当对象收到无法响应或转发的 aSelector 消息时,运行时系统就会调用此方法。”
- (instancetype) init {
[self release];
[super doesNotRecognizeSelector:_cmd];
return nil;
}
NSAssert
NSAssert
Use NSAssertto throw NSInternalInconsistencyException and show a message:
使用NSAssert抛出NSInternalInconsistencyException并显示一条消息:
- (instancetype) init {
[self release];
NSAssert(false,@"unavailable, use initWithBlah: instead");
return nil;
}
raise:format:
raise:format:
Use raise:format:to throw your own exception:
使用raise:format:抛出自己的异常:
- (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.
[self release]需要,因为对象已经被alloc吃掉了。使用 ARC 时,编译器会为您调用它。无论如何,当您要有意停止执行时,不必担心。
objc_designated_initializer
objc_designated_initializer
In case you intend to disable initto force the use of a designated initializer, there is an attribute for that:
如果您打算禁用init强制使用指定的初始化程序,则有一个属性:
-(instancetype)myOwnInit NS_DESIGNATED_INITIALIZER;
This generates a warning unless any other initializer method calls myOwnInitinternally. Details will be published in Adopting Modern Objective-Cafter next Xcode release (I guess).
除非任何其他初始化方法在myOwnInit内部调用,否则这会生成警告。详细信息将在下一个 Xcode 版本发布后发布在采用现代 Objective-C 中(我猜)。
回答by lehn0058
Apple has started using the following in their header files to disable the init constructor:
Apple 已开始在其头文件中使用以下内容来禁用 init 构造函数:
- (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).
这在 Xcode 中正确显示为编译器错误。具体来说,这是在他们的几个 HealthKit 头文件中设置的(HKUnit 就是其中之一)。
回答by Nathan Kinsinger
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.
如果您在谈论默认的 -init 方法,那么您就不能。它是从 NSObject 继承的,每个类都会响应它而不会发出警告。
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.
您可以创建一个新方法,例如 -initMyClass,并将其放入 Matt 建议的私有类别中。然后定义默认的 -init 方法,以在调用时引发异常,或者(更好)使用一些默认值调用您的私有 -initMyClass。
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).
人们似乎想要隐藏 init 的主要原因之一是为了单例对象。如果是这种情况,则不需要隐藏 -init,只需返回单例对象即可(如果尚不存在,则创建它)。
回答by Jerry Juang
Put this in header file
把它放在头文件中
- (id)init UNAVAILABLE_ATTRIBUTE;
回答by Kaunteya
You can declare any method to be not available using NS_UNAVAILABLE.
您可以使用 声明任何方法不可用NS_UNAVAILABLE。
So you can put these lines below your @interface
所以你可以把这些行放在你的@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
回答by Peter Lapisu
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
好吧,为什么你不能让它“私有/不可见”的问题是导致 init 方法被发送到 id(因为 alloc 返回一个 id)而不是 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...)
请注意,从编译器(检查器)的角度来看,id 可以潜在地响应任何输入的内容(它无法检查运行时真正进入 id 的内容),因此您只能在没有任何地方(publicly = in)时隐藏 init头文件)使用一个方法 init,而不是编译器知道的,id 无法响应 init,因为在任何地方都没有 init(在你的源代码中,所有的库等......)
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
所以你不能禁止用户通过 init 并被编译器粉碎......但是你可以做的是阻止用户通过调用 init 获得一个真实的实例
simply by implementing init, which returns nil and have an (private / invisible) initializer which name somebody else won't get (like initOnce, initWithSpecial ...)
简单地通过实现 init,它返回 nil 并有一个(私有/不可见的)初始化器,它的名字别人不会得到(比如 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
并且它实际上会返回一个新实例,但是如果 initOnce 在我们的项目中没有任何地方被公开(在标题中)声明,它将生成一个警告(id 可能不会响应......)并且无论如何使用它的人都需要确切地知道真正的初始化程序是 initOnce
we could prevent this even further, but there is no need
我们可以进一步防止这种情况,但没有必要
回答by Matt Dillard
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.
这取决于您所说的“设为私有”是什么意思。在 Objective-C 中,在对象上调用方法可能更好地描述为向该对象发送消息。语言中没有任何内容禁止客户端调用对象上的任何给定方法;最好不要在头文件中声明方法。如果客户端仍然使用正确的签名调用“私有”方法,它仍将在运行时执行。
That said, the most common way to create a private method in Objective-C is to create a Categoryin the implementation file, and declare all of the "hidden" methods in there. Remember that this won't truly prevent calls to initfrom running, but the compiler will spit out warnings if anyone tries to do this.
也就是说,在 Objective-C 中创建私有方法的最常见方法是在实现文件中创建一个类别,并在其中声明所有“隐藏”方法。请记住,这不会真正阻止调用init运行,但如果有人试图这样做,编译器会发出警告。
MyClass.m
我的课堂.m
@interface MyClass (PrivateMethods)
- (NSString*) init;
@end
@implementation MyClass
- (NSString*) init
{
// code...
}
@end
There's a decent threadon MacRumors.com about this topic.
MacRumors.com 上有一个关于这个主题的不错的帖子。
回答by techniao
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 __unavailableas Jano explained for his first example.
我会建议使用__unavailable作为Jano的他的第一个例子来说明。
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:
想象一下 -initWithLessParameters 会发生什么,如果我在子类中这样做:
- (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.)
这意味着您应该倾向于使用私有(隐藏)方法,尤其是在初始化方法中,除非您计划覆盖这些方法。但是,这是另一个主题,因为您并不总是完全控制超类的实现。(这让我质疑使用 __attribute((objc_designated_initializer)) 作为不好的做法,尽管我没有深入使用它。)
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)
它还意味着您可以在必须在子类中覆盖的方法中使用断言和异常。(在 Objective-C中创建抽象类中的“抽象”方法)
And, don't forget about the +new class method.
并且,不要忘记 +new 类方法。

