回答:
SmalltalkのようなObjective-Cには、「プライベート」メソッドと「パブリック」メソッドの概念はありません。任意のメッセージをいつでも任意のオブジェクトに送信できます。
あなたができることは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;
}
もう1つの代替案-おそらく実際にははるかに優れている-は、-init
可能であれば、クラスに対して賢明なことを行うことです。
シングルトンオブジェクトが使用されていることを「確認」しようとしているためにこれを実行しようとしている場合は、気にしないでください。具体的には、「オーバーライドと気にしないでください+allocWithZone:
、-init
、-retain
、-release
シングルトンを作成する」方法。これは事実上常に不要であり、複雑さを増すだけで実際には大きな利点はありません。
代わりに、+sharedWhatever
メソッドがシングルトンにアクセスする方法となるようにコードを記述し、それをヘッダー内のシングルトンインスタンスを取得する方法として文書化します。ほとんどの場合、それで十分です。
alloc
しinit
、適切なクラスを持っているが間違ったインスタンスを持っているためにコードが正しく機能しない可能性があります。これは、OO におけるカプセル化の原則の本質です。APIで、他のクラスが必要としない、またはアクセスする必要がないものを非表示にします。すべてを公開し続けるだけでなく、人間がすべてを追跡することを期待します。
NS_UNAVAILABLE
- (instancetype)init NS_UNAVAILABLE;
これは、利用できない属性の短いバージョンです。それはmacOS 10.7とiOS 5で最初に登場しました。NSObjCRuntime.hでとして定義されてい#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
ます。
ObjCコードではなく、Swiftクライアントに対してのみメソッドを無効にするバージョンがあります。
- (instancetype)init NS_SWIFT_UNAVAILABLE;
unavailable
unavailable
ヘッダーに属性を追加して、initの呼び出し時にコンパイラエラーを生成します。
-(instancetype) init __attribute__((unavailable("init not available")));
理由がない場合は、と入力__attribute__((unavailable))
するか、さらには__unavailable
:
-(instancetype) __unavailable init;
doesNotRecognizeSelector:
doesNotRecognizeSelector:
NSInvalidArgumentException を発生させるために使用します。「オブジェクトが応答または転送できないaSelectorメッセージを受信すると、ランタイムシステムはこのメソッドを呼び出します。」
- (instancetype) init {
[self release];
[super doesNotRecognizeSelector:_cmd];
return nil;
}
NSAssert
NSAssert
NSInternalInconsistencyExceptionをスローしてメッセージを表示するために使用します。
- (instancetype) init {
[self release];
NSAssert(false,@"unavailable, use initWithBlah: instead");
return nil;
}
raise:format:
raise:format:
独自の例外をスローするために使用します。
- (instancetype) init {
[self release];
[NSException raise:NSGenericException
format:@"Disabled. Use +[[%@ alloc] %@] instead",
NSStringFromClass([self class]),
NSStringFromSelector(@selector(initWithStateDictionary:))];
return nil;
}
[self release]
オブジェクトはすでにalloc
食べられているので必要です。ARCを使用すると、コンパイラーがそれを呼び出します。いずれにしても、意図的に実行を停止しようとしているときに心配する必要はありません。
objc_designated_initializer
init
指定したイニシャライザの使用を強制的に無効にする場合は、そのための属性があります。
-(instancetype)myOwnInit NS_DESIGNATED_INITIALIZER;
これにより、他の初期化メソッドがmyOwnInit
内部で呼び出されない限り、警告が生成されます。詳細は、次のXcodeリリース後に、Adopting Modern Objective-Cで公開されると思います(多分)。
init
。このメソッドが無効な場合、なぜオブジェクトを初期化するのですか?さらに、例外をスローする場合init*
、開発者に正しいメソッドを伝えるカスタムメッセージを指定できますが、の場合はそのようなオプションはありませんdoesNotRecognizeSelector
。
Appleは、ヘッダーファイルで以下を使用して、initコンストラクタを無効にし始めました。
- (instancetype)init NS_UNAVAILABLE;
これは、Xcodeでコンパイラエラーとして正しく表示されます。具体的には、これはいくつかのHealthKitヘッダーファイルで設定されています(HKUnitはその1つです)。
デフォルトの-initメソッドについて話している場合はできません。これはNSObjectから継承され、すべてのクラスが警告なしでそれに応答します。
-initMyClassなどの新しいメソッドを作成し、Mattが提案するようにプライベートカテゴリに配置することができます。次に、デフォルトの-initメソッドを定義して、呼び出された場合に例外を発生させるか、デフォルト値を指定してプライベート-initMyClassを呼び出します。
initを非表示にしたいと思われる主な理由の1つは、シングルトンオブジェクトです。その場合は、-initを非表示にする必要はなく、代わりにシングルトンオブジェクトを返すだけです(まだ存在しない場合は作成します)。
これをヘッダーファイルに入れます
- (id)init UNAVAILABLE_ATTRIBUTE;
を使用して、使用できないメソッドを宣言できますNS_UNAVAILABLE
。
したがって、これらの行を@interfaceの下に置くことができます
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
接頭辞ヘッダーでマクロをさらに定義する
#define NO_INIT \
- (instancetype)init NS_UNAVAILABLE; \
+ (instancetype)new NS_UNAVAILABLE;
そして
@interface YourClass : NSObject
NO_INIT
// Your properties and messages
@end
それは、「プライベートにする」という意味によって異なります。Objective-Cでは、オブジェクトのメソッドを呼び出すことは、そのオブジェクトにメッセージを送信することとして説明する方がよいでしょう。クライアントがオブジェクトの特定のメソッドを呼び出すことを禁止する言語には何もありません。最善の方法は、ヘッダーファイルでメソッドを宣言しないことです。それにもかかわらず、クライアントが適切な署名を使用して「プライベート」メソッドを呼び出した場合でも、実行時に実行されます。
つまり、Objective-Cでプライベートメソッドを作成する最も一般的な方法は、実装ファイルでカテゴリを作成し、その中ですべての「非表示」メソッドを宣言することです。これは本当に呼び出しを妨げることはありませんinit
実行が、誰かがこれを行おうとするとコンパイラは警告を吐き出します。
MyClass.m
@interface MyClass (PrivateMethods)
- (NSString*) init;
@end
@implementation MyClass
- (NSString*) init
{
// code...
}
@end
このトピックについてMacRumors.comにまともなスレッドがあります。
「プライベート/非表示」にすることができない理由は、initメソッドがYourClassにではなく、(allocがIDを返すので)IDに送信するためです。
コンパイラー(チェッカー)の観点から、IDはこれまでに入力されたものに潜在的に応答する可能性がある(実行時に実際にIDに入る内容をチェックできない)ことに注意してください。 header)initメソッドを使用します。コンパイルでわかるように、idがinitに応答する方法はありません。これは、initがどこにもないためです(ソース、すべてのライブラリなど)。
したがって、ユーザーがinitを渡してコンパイラーによって破壊されることを禁止することはできません...しかし、あなたができることは、ユーザーがinitを呼び出して実際のインスタンスを取得できないようにすることです
nilを返し、他の誰かが取得できない(プライベート/非表示)イニシャライザを持っているinitを実装するだけで(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;
}
注:誰かがこれを行うことができた
SomeClass * c = [[SomeClass alloc] initOnce];
実際には新しいインスタンスを返しますが、initOnceがプロジェクトのどこにも(ヘッダーで)公に宣言されていない場合、警告が生成され(IDが応答しない場合があります...)、とにかくこれを使用しているユーザーは、実際の初期化子がinitOnceであることを正確に知る
これをさらに防ぐことができますが、必要はありません
サブクラスでメソッドを非表示にするためにアサーションを配置し、例外を発生させることは、意図したとおりの厄介なトラップであることを言及しなければなりません。
私が使用することをお勧めします__unavailable
ようJanoが彼の最初の例で説明しました。
メソッドはサブクラスでオーバーライドできます。これは、スーパークラスのメソッドがサブクラスで例外を発生させるだけのメソッドを使用する場合、おそらく意図したとおりに機能しないことを意味します。言い換えれば、これまで機能していたものを壊してしまいました。これは、初期化メソッドにも当てはまります。このようなかなり一般的な実装の例を次に示します。
- (SuperClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
...bla bla...
return self;
}
- (SuperClass *)initWithLessParameters:(Type1 *)arg1
{
self = [self initWithParameters:arg1 optional:DEFAULT_ARG2];
return self;
}
サブクラスでこれを行うと、-initWithLessParametersがどうなるか想像してみてください。
- (SubClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
[self release];
[super doesNotRecognizeSelector:_cmd];
return nil;
}
これは、メソッドをオーバーライドする予定がない限り、特に初期化メソッドではプライベート(非表示)メソッドを使用する傾向があることを意味します。ただし、スーパークラスの実装を常に完全に制御できるわけではないため、これは別のトピックです。(これにより、__ attribute((objc_designated_initializer))の使用は悪い習慣として疑問視されます。
また、サブクラスでオーバーライドする必要があるメソッドでアサーションと例外を使用できることも意味します。(Objective-Cでの抽象クラスの作成と同様の「抽象」メソッド)
+新しいクラスメソッドを忘れないでください。
NS_UNAVAILABLE
。一般的には、このアプローチを使用することをお勧めします。OPは承認された回答を改訂することを検討しますか?ここでの他の回答は多くの有用な詳細を提供しますが、これを達成するための好ましい方法ではありません。