如何在NSObject中禁止基本的init方法

Ant*_*ony 17 objective-c nsobject ios

我想强制用户使用我自己的init方法(例如-(id)initWithString:(NSString*)foo;)而不是基本的[[myObject alloc]init];.

我怎样才能做到这一点?

fzw*_*zwo 20

这里的所有其他答案都已过时.有一种方法可以正确地做到这一点!

虽然在某人调用您的方法时很容易在运行时崩溃,但编译时检查会更好.

幸运的是,这在Objective-C中已经有一段时间了.

使用LLVM,您可以将类中的任何方法声明为不可用

- (void)aMethod __attribute__((unavailable("This method is not available")));
Run Code Online (Sandbox Code Playgroud)

这将使编译器在尝试调用时抱怨aMethod.大!

由于- (id)init这只是一种普通的方法,因此您可以禁止以这种方式调用默认(或任何其他)初始化程序.

但请注意,这不能保证使用语言的动态方面调用方法,例如通过[object performSelector:@selector(aMethod)]等.在init的情况下,您甚至不会收到警告,因为init方法在其他方法中定义类,编译器不知道足以给你一个未声明的选择器警告.

因此,为了确保这一点,请确保在调用时init方法崩溃(参见Adam的回答).

如果你想- (id)init在框架中禁止+ (id)new,请确保也禁止,因为这只会转发到init.

Javi Soto编写了一个小宏来禁止使用指定的初始化程序更快更容易并提供更好的消息.你可以在这里找到它.

  • 请注意,GCC用户 - 这个**不起作用**,让GNUStep在非达尔文框下使用clang是一件痛苦的事. (2认同)

Cod*_*der 14

TL; 博士

斯威夫特:

private init() {}
Run Code Online (Sandbox Code Playgroud)

由于默认情况下所有Swift类都包含一个内部init,因此您可以将其更改为private以防止其他类调用它.

目标C:

把它放在你班级的.h文件中.

- (instancetype)init NS_UNAVAILABLE;
Run Code Online (Sandbox Code Playgroud)

这依赖于OS定义,该定义阻止调用命名的方法.


Ada*_*dam 9

接受的答案是错误的 - 你可以做到这一点,这很容易,你只需要有点明确.这是一个例子:

你有一个名为"DontAllowInit"的类,你想阻止人们初始化:

@implementation DontAllowInit

- (id)init
{
    if( [self class] == [DontAllowInit class])
    {
        NSAssert(false, @"You cannot init this class directly. Instead, use a subclass e.g. AcceptableSubclass");

        self = nil; // as per @uranusjr's answer, should assign to self before returning
    }
    else
        self = [super init];

    return nil;
}
Run Code Online (Sandbox Code Playgroud)

说明:

  1. 当你调用[super init]时,分配的类是SUBCLASS.
  2. "self"是实例 - 即init的东西
  3. "[self class]"是实例化的类- 当SUBCLASS调用[super init]时它将是SUBCLASS,或者当使用普通[[SuperClass alloc] init]调用SUPERCLASS时将是SUPERCLASS
  4. 因此,当超类接收到"init"调用时,它只需要检查alloc'd类是否与它自己的类相同

完美的工作.注意:我不推荐这种技术用于"普通应用",因为通常你INSTEAD想要使用协议.

但是......在编写库时......这种技术非常有价值:你经常想要"保存(其他开发人员)自己",并且很容易让NSAssert告诉他们"哎呀!你试图分配/初始化错误的类!试试X级......".