如何禁止 NSObject 中的基本 init 方法
我想强制用户使用我自己的 init 方法(例如 -(id)initWithString:(NSString*)foo;
),而不是基本的 [[myObject alloc]init];< /代码>。
我怎样才能做到这一点?
I want to force user to use my own init method (for example -(id)initWithString:(NSString*)foo;
) and not the basic [[myObject alloc]init];
.
how can I do that?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
这里的所有其他答案都已过时。现在有一种方法可以正确地做到这一点!
虽然当有人调用您的方法时很容易在运行时崩溃,但编译时检查会更好。
幸运的是,这在 Objective-C 中已经成为可能一段时间了。
使用 LLVM,您可以将类中的任何方法声明为不可用,如下所示。
这将使编译器在尝试调用
aMethod
时发出警告。伟大的!由于
- (id)init
只是一个普通方法,因此您可以通过这种方式禁止调用默认(或任何其他)初始化程序。但请注意,这不能确保使用语言的动态方面调用方法,例如通过
[object PerformSelector:@selector(aMethod)]
等在 init 的情况下,您甚至不会收到警告,因为 init 方法是在其他类中定义的,并且编译器不知道足够的信息来向您提供未声明的选择器警告。因此,为了防止这种情况发生,请确保 init 方法在被调用时崩溃(请参阅 Adam 的回答)。
如果您想在框架中禁止
- (id)init
,请确保也禁止+ (id)new
,因为这只会转发到 init。 JaviSoto 编写了一个小宏,可以更快、更轻松地禁止使用指定的初始化程序,并提供更好的消息。您可以在此处找到它。
All other answers here are outdated. There is a way to do this properly now!
While it is easy to just crash at runtime when somebody calls your method, compile-time checking would be far preferable.
Fortunately, this has been possible in Objective-C for a while.
Using LLVM, you can declare any method as unavailable in a class like so
This will make the compiler complain when trying to call
aMethod
. Great!Since
- (id)init
is just an ordinary method, you can prohibit calling of the default (or any other) initializer in this way.Note, though, that this will not insure against the method being called using the dynamic aspects of the language, for instance via
[object performSelector:@selector(aMethod)]
etc. In the case of init, you won't even get a warning, because the init method is defined in other classes, and the compiler doesn't know enough to give you an undeclared selector warning.So, to ensure against this, make sure that the init method crashes when being called (see Adam's answer).
If you want to disallow
- (id)init
in a framework, make sure to also disallow+ (id)new
, as this will just forward to init.Javi Soto has written a small macro to forbid using the designated initializer faster and easier and to give nicer messages. You can find it here.
TL; dr
Swift:
由于所有 Swift 类默认都包含一个内部 init,因此您可以将其更改为私有以防止其他类调用它。
目标 C:
将其放入类的 .h 文件中。
这依赖于操作系统定义,该定义阻止调用指定的方法。
tl; dr
Swift:
Since all Swift classes include an internal init by default, you can change it to private to keep other classes from calling it.
Objective C:
Put this in your class's .h file.
This relies on an OS define that prevents the method named from being called.
接受的答案是不正确的 - 你可以做到这一点,而且非常简单,你只需要明确一点即可。下面是一个示例:
您有一个名为“DontAllowInit”的类,您希望阻止人们对其进行初始化:
说明:
工作完美。注意:我不建议将这种技术用于“普通应用程序”,因为通常您想使用协议。
然而......在编写库时......这种技术非常有价值:你经常想要“从他们自己手中拯救(其他开发人员)”,并且很容易 NSAssert 并告诉他们“哎呀!你试图分配/初始化错误的类”尝试 X 类......”。
The accepted answer is incorrect - you CAN do this, and it's very easy, you just have to be a bit explicit. Here's an example:
You have a class named "DontAllowInit" which you want to prevent people init'ing:
Explanation:
Works perfectly. NB: I don't recommend this technique for "normal apps" because usually you INSTEAD want to use a Protocol.
HOWEVER ... when writing Libraries ... this technique is VERY valuable: you frequently want to "save (other developers) from themselves", and its easy to NSAssert and tell them "Oops! you tried to alloc/init the wrong class! Try class X instead...".
如果您记录不允许
-init
并因此使用它是程序员错误,则抛出异常是合理的。然而,更好的答案是让-init
使用一些合适的默认值调用-initWtihString:
,即Throwing the exception is justified if you document that
-init
is not allowed and therefore using it is a programmer error. However, a better answer would be to make-init
invoke-initWtihString:
with some suitable default value i.e.简短的回答:你不能。
更长的答案:最佳实践是将最详细的初始化程序设置为指定的初始化程序,如所述 这里。然后“init”将使用合理的默认值调用该初始化程序。
另一种选择是“assert(0)”或在“init”内以另一种方式崩溃,但这不是一个好的解决方案。
Short answer: you can't.
Longer answer: the best practice is to set your most detailed initializer as the designated initializer, as described here. 'init' will then call that initializer with sane, default values.
Another option is to 'assert(0)' or crash in another way inside the 'init', but this isn't a good solution.
实际上我投票赞成了亚当的答案,但想添加一些内容。
首先,强烈建议您检查
self
与nil<(如
NSObject
子类中自动生成的init
方法所示)。 /code> 在init
中。另外,我不认为类对象保证像==
中那样“相等”。我这样做更像是注意,我使用
isKindOfClass:
代替,因为恕我直言,如果此类不允许init
,它也应该不允许其后代拥有它。如果它的子类之一想要它回来(这对我来说没有意义),它应该通过调用我指定的初始值设定项来显式覆盖它。但更重要的是,无论您是否采用上述方法,您都应该始终拥有适当的文档。您应该始终清楚地说明哪个方法是您指定的初始化程序,尽最大努力提醒其他人不要在文档中使用不适当的初始化程序,并信任其他用户/开发人员,而不是试图“拯救其他人的屁股”聪明的代码。
I actually voted up Adam's answer, but would like to add some things to it.
First, it is strongly encouraged (as seem in auto-generated
init
methods inNSObject
subclasses) that you checkself
againstnil
ininit
s. Also, I don't think class objects are guaranteed to be "equal" as in==
. I do this more likeNote that I use
isKindOfClass:
instead because IMHO if this class disallowsinit
, it should disallow its descendants to have it as well. If one of its subclass want it back (which doesn't make sense for me), it should override it explicitly by calling my designated initializer.But more importantly, whether you take the above approach or not, you should always have appropriate documentation. You should always clearly state which method is your designated initializer, try as best as you can to remind others not to use inappropriate initializers in documentation, and put some faith in other users/developers, instead of trying to "save everybody else's asses" with clever codes.
对于 Swift 5,使用
@available(*, unavailable, message: "This method is not available")
是一种方法,但在这种方法中,您不能在类内使用实例方法。我建议对无法访问的初始化程序使用 private 关键字。对于继承自 NSObject 的类:
For Swift 5, using
@available(*, unavailable, message: "This method is not available")
is a way but in this approach you can't use the instance method inside the class. I suggest using private keyword for inaccesible initializers.For a class that inherits from NSObject: