我想强制用户使用我自己的init方法(例如-(id)initWithString:(NSString*)foo;
)而不是基本的[[myObject alloc]init];
。
我该怎么做?
答案 0 :(得分:20)
虽然在某人调用您的方法时很容易在运行时崩溃,但编译时检查将更为可取。
幸运的是,在Objective-C中已经有一段时间了。
使用LLVM,您可以在类似
的类中声明任何方法不可用- (void)aMethod __attribute__((unavailable("This method is not available")));
这会使编译器在尝试调用aMethod
时抱怨。太好了!
由于- (id)init
只是一种普通方法,因此您可以禁止以这种方式调用默认(或任何其他)初始值设定项。
但请注意,这不会保证使用该语言的动态方面来调用该方法,例如通过[object performSelector:@selector(aMethod)]
等。在init的情况下,您赢了甚至得到一个警告,因为init方法是在其他类中定义的,编译器不知道给你一个未声明的选择器警告。
因此,为了确保这一点,请确保在调用时init方法崩溃(请参阅Adam's answer)。
如果您想在框架中禁止- (id)init
,请务必同时禁止使用+ (id)new
,因为这只会转发给初始化。
Javi Soto编写了一个小宏来禁止使用指定的初始化程序更快更容易并提供更好的消息。你可以找到它here。
答案 1 :(得分:12)
TL;医生
<强>夫特强>:
private init() {}
由于默认情况下所有Swift类都包含一个内部init,因此您可以将其更改为private以防止其他类调用它。
目标C:
将它放在班级的.h文件中。
- (instancetype)init NS_UNAVAILABLE;
这依赖于一个操作系统定义,它阻止了被调用的方法。
答案 2 :(得分: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;
}
说明:
完美无缺。注意:我不推荐这种技术用于“普通应用程序”,因为通常你INSTEAD想要使用协议。
但是......在编写库时...这种技术非常有价值:你经常想“自己保存(其他开发人员)”,并且易于NSAssert并告诉他们“哎呀!你试过alloc / init错误的类!尝试使用X类......“。
答案 3 :(得分:3)
-(id) init
{
@throw [NSException exceptionWithName: @"MyExceptionName"
reason: @"-init is not allowed, use -initWithString: instead"
userInfo: nil];
}
-(id) initWithString: (NSString*) foo
{
self = [super init]; // OK because it calls NSObject's init, not yours
// etc
如果您声明不允许-init
,则抛出异常是合理的,因此使用它是程序员错误。但是,更好的答案是让-init
使用一些合适的默认值调用-initWtihString:
,即
-(id) init
{
return [self initWithString: @""];
}
答案 4 :(得分:0)
我实际上投了Adam的答案,但想补充一些东西。
首先,强烈建议您(init
子类中的自动生成的NSObject
方法)在self
nil
中init
查看==
秒。另外,我认为类对象不能保证与- (id)init
{
NSAssert(NO, @"You are doing it wrong.");
self = [super init];
if ([self isKindOfClass:[InitNotAllowedClass class]])
self = nil;
return self;
}
中的“相等”。我这样做更像是
isKindOfClass:
请注意,我使用init
代替因为恕我直言,如果此类不允许{{1}},它应该禁止其后代也拥有它。如果它的一个子类想要它(这对我来说没有意义),它应该通过调用我指定的初始化器显式覆盖它。
但更重要的是,无论您是否采用上述方法,都应始终拥有相应的文档。您应该始终清楚地说明哪种方法是您指定的初始化程序,尽可能地尝试提醒其他人不要在文档中使用不适当的初始化程序,并对其他用户/开发人员有所信任,而不是试图“拯救其他人的驴子”聪明的代码。
答案 5 :(得分:-1)
简短回答:你不能。
更长的答案:最佳做法是将最详细的初始化程序设置为指定的初始值设定项,如here所述。然后'init'将使用合理的默认值调用该初始化程序。
另一种选择是'断言(0)'或在'init'中以另一种方式崩溃,但这不是一个好的解决方案。