Objective-Cで抽象クラスを作成する


507

私はもともとJavaプログラマーで、現在Objective-Cを使用しています。抽象クラスを作成したいのですが、Objective-Cではそれができないようです。これは可能ですか?

そうでない場合、Objective-Cで抽象クラスにどのくらい近づくことができますか?


18
以下の答えは素晴らしいです。これは、抽象クラスの問題がプライベートメソッドに接線的に関連していることです。どちらもクライアントコードで実行できることを制限するためのメソッドであり、どちらもObjective-Cには存在しません。言語自体の考え方はJavaとは根本的に異なることを理解するのに役立つと思います。私の回答をご覧ください: stackoverflow.com/questions/1020070/#1020330
Quinn Taylor

他の言語とは対照的に、Objective-Cコミュニティの考え方に関する情報をありがとう。それは本当に私が持っていた多くの関連する質問を解決します(なぜプライベートメソッドのための簡単なメカニズムがないなど)。
ジョナサンアーボガスト

1
それで、Java比較を提供するCocoaDevサイトを見てくださいcocoadev.com/index.pl?AbstractSuperClass

2
バリーはそれを後から考えると述べていますが(間違って読んだ場合は許してください)、目的Cのプロトコルを探していると思います。たとえば、「プロトコルとは何ですか?」を参照してください
jww 2014

回答:


633

通常、Objective-Cクラスは規則によってのみ抽象です。作成者がクラスを抽象として文書化する場合は、サブクラス化せずに使用しないでください。ただし、抽象クラスのインスタンス化を妨げるコンパイル時の強制はありません。実際、ユーザーがカテゴリーを介して(つまり、実行時に)抽象メソッドの実装を提供することを妨げるものはありません。抽象クラスのメソッド実装で例外を発生させることにより、ユーザーに少なくとも特定のメソッドをオーバーライドさせることができます。

[NSException raise:NSInternalInconsistencyException 
            format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];

メソッドが値を返す場合、それは少し使いやすいです

@throw [NSException exceptionWithName:NSInternalInconsistencyException
                               reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
                             userInfo:nil];

そのため、メソッドからreturnステートメントを追加する必要はありません。

抽象クラスが本当にインターフェースである(つまり、具体的なメソッド実装がない)場合は、Objective-Cプロトコルを使用する方が適切なオプションです。


18
答えの最も適切な部分は、メソッドを定義する代わりに@protocolを使用できることを言及したことだと思います。
チャドスチュワート

13
明確にするために:定義でメソッドを宣言@protocolできますが、そこでメソッドを定義することはできません。
Richard

NSExceptionのカテゴリメソッドを使用すると、+ (instancetype)exceptionForCallingAbstractMethod:(SEL)selectorこれは非常にうまく機能します。
Patrick Pijnappel 2014

これは私にとってうまくいきました。なぜなら、私見では、例外をスローすることは、これが望ましい動作であるというより、他の開発者にとってより明白ですdoesNotRecognizeSelector
クリス

抽象クラスは、ここで与えられるような部分的な実装/フローロジック有するまたはテンプレートメソッドパターン(完全に抽象クラス用)使用プロトコルstackoverflow.com/questions/8146439/...~~Vを。以下の私の答えを参照してください。
hadaytullah 2015

267

いいえ、Objective-Cで抽象クラスを作成する方法はありません。

メソッド/セレクターがdoesNotRecognizeSelector:を呼び出すようにすることで、抽象クラスをモックできます。したがって、例外を発生させて、クラスを使用できなくします。

例えば:

- (id)someMethod:(SomeObject*)blah
{
     [self doesNotRecognizeSelector:_cmd];
     return nil;
}

initに対してもこれを行うことができます。


5
@Chuck、私は反対投票しませんでしたが、NSObjectリファレンスは、メソッドのオーバーライドを強制するのではなく、メソッドを継承したくない場合にこれを使用することを提案しています。同じことかもしれませんが、たぶん:)
Dan Rosenstarkが

このインスタンスでプロトコルを使用したくないのはなぜですか?私にとって、これはメソッドスタブについてのみ知っておくと便利ですが、抽象クラス全体については知りません。この使用例は限られているようです。
lewiguez 2014年

私はそれに反対票を投じませんでしたが、あなたがinitメソッドで例外を発生させるべきだという提案がその最も可能性の高い理由です。サブクラスの最も一般的な形式は、self = [super init]を呼び出すことで独自のinitメソッドを開始します-これは素直に例外をスローします。このフォーマットはほとんどのメソッドでうまく機能しますが、サブクラスがそれをスーパー実装と呼ぶかもしれないところでは、私は決してそれをしません。
Xono 2014

5
Objective-Cで抽象クラスを作成することは絶対に可能ですが、それは非常に一般的です。これを行うAppleフレームワーククラスは多数あります。Appleの抽象クラスは、多くの場合NSInvalidAbstractInvocation()を呼び出すことにより、特定の例外(NSInvalidArgumentException)をスローします。抽象メソッドの呼び出しはプログラミングエラーであるため、例外がスローされます。抽象ファクトリは通常、クラスクラスタとして実装されます。
2014

2
@quellish:あなたが言ったように:抽象メソッドの呼び出しはプログラミングエラーです。ランタイムエラーレポート(NSException)に依存するのではなく、そのように扱う必要があります。これは、Obj-Cで抽象が「このタイプのオブジェクトをインスタンス化できない」ことを意味する他の言語からの開発者にとって最大の問題だと思います。
Cross_ 2015

60

上記の@Barry Warkの答えをリフして(そしてiOS 4.3にアップデートして)、これを自分の参照用に残します:

#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
#define methodNotImplemented() mustOverride()

あなたの方法ではこれを使うことができます

- (void) someMethod {
     mustOverride(); // or methodNotImplemented(), same thing
}



注:マクロをC関数のように見せるのが良い考えかどうかはわかりませんが、逆に学習するまでそれを保持します。呼び出されたことに応じてランタイムシステムがスローするので(を参照するNSInvalidArgumentExceptionよりNSInternalInconsistencyException)、使用する方が正しいと思いますdoesNotRecognizeSelectorNSObjectドキュメントを参照)。


1
確かに、@ TomA、マクロできる他のコードのアイデアが得られることを願っています。私が最もよく使用するマクロは、シングルトンへの単純な参照です。コードは言うuniverse.thingが、それはに拡張され[Universe universe].thingます。何千ものコードの文字を節約してとても楽しい...
Dan Rosenstark '21

すごい。ただし、少し変更しました#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
noamtm 2012

1
@年:私はそうは思いません。__PRETTY_FUNCTION__ここで推奨されているように、DLog(...)マクロを介して、あらゆる場所で使用します:stackoverflow.com/a/969291/38557
noamtm 2012

1
詳細については–#define setMustOverride() NSLog(@"%@ - method not implemented", NSStringFromClass([self class])); mustOverride()
gbk

1
基本クラスを追加してからこのクラスをたとえば10回継承し、クラスの1つにこれを実装するのを忘れたTerminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BaseDynamicUIViewController localizeUI] must be overridden in a subclass/category'場合、継承されていない基本クラスの名前のメッセージが表示されますHomeViewController - method not implemented。より多くの情報を提供します
gbk

42

私が思いついた解決策は:

  1. 「抽象」クラスで必要なすべてのプロトコルを作成する
  2. プロトコルを実装する基本クラスを作成します(または抽象クラスと呼ぶこともあります)。「抽象的」にしたいすべてのメソッドについて、.hファイルではなく.mファイルに実装します。
  3. 子クラスに基本クラスを継承させ、プロトコルを実装させます。

このようにして、コンパイラーは、子クラスによって実装されていないプロトコルのメソッドに対して警告を表示します。

これはJavaほど簡潔ではありませんが、必要なコンパイラ警告が表示されます。


2
+1これは本当に、Javaの抽象クラスに最も近いソリューションです。私はこのアプローチを自分で使用しましたが、うまくいきます。プロトコルに基本クラスと同じ名前を付けることもできます(Appleがで行ったようにNSObject)。その後、プロトコルと基本クラス宣言を同じヘッダーファイルに配置すると、抽象クラスとほとんど区別がつかなくなります。
codingFriend1

ああ、でも私の抽象クラスはプロトコルの一部を実装していますが、残りはサブクラスによって実装されています。
Roger CS Wernersson 2013年

1
子クラスだけにプロトコルを実装させ、スーパークラスのメソッドを空白ではなくまとめて残すことができます。次に、タイプSuperclass <MyProtocol>のプロパティがあります。また、柔軟性を高めるために、プロトコルのメソッドの前に@optionalを付けることができます。
dotToString 2013年

これはXcode 5.0.2では機能しないようです。「抽象」クラスの警告を生成するだけです。「抽象」クラスを拡張しないと、適切なコンパイラ警告が生成されますが、メソッドを継承することはできません。
Topher Fangio

私はこのソリューションを好みますが、本当に好きではありません。プロジェクトのコード構造としては、あまり良いものではありません。 //これをサブクラスの基本型として使用する必要があります。typedef BaseClass <BaseClassProtocol> BASECLASS; それはほんの一週間のルールです、私はそれが好きではありません。
イタチ

35

Omni Groupメーリングリストから:

現時点では、Objective-CにはJavaのような抽象的なコンパイラ構造はありません。

したがって、行うのは、抽象クラスを他の通常のクラスとして定義し、空であるか、セレクターの非サポートを報告する抽象メソッドのメソッドスタブを実装することだけです。例えば...

- (id)someMethod:(SomeObject*)blah
{
     [self doesNotRecognizeSelector:_cmd];
     return nil;
}

また、デフォルトの初期化子を介して抽象クラスが初期化されないようにするために、次のことも行います。

- (id)init
{
     [self doesNotRecognizeSelector:_cmd];
     [self release];
     return nil;
}

1
私は-doesNotRecognizeSelectorを使用することを考えていませんでした:いくつかの点でこのアプローチが好きです。この方法または例外を発生させることによって作成された「抽象」メソッドに対してコンパイラーに警告を発行させる方法を誰かが知っていますか?それは素晴らしいだろう...
クインテイラー

26
doesNotRecognizeSelectorアプローチは、Appleが提案するself = [super init]パターンを防ぎます。
dlinsin 2009

1
@david:できるだけ早く例外を発生させることがポイントだと確信しています。コンパイル時に実行するのが理想的ですが、それを行う方法がないため、実行時の例外に落ち着きました。これはアサーションの失敗に似ており、最初から本番用のコードで発生させることはできません。実際、このコードを実行してはならないことが明確になっているため、assert(false)の方が実際には優れている場合があります。ユーザーはそれを修正するために何もできません。開発者が修正する必要があります。したがって、ここで例外またはアサーションエラーを発生させることは、良い考えのように思えます。
センスフル2010

2
サブクラスが[super init]への完全に正当な呼び出しを行う可能性があるため、これは実行可能な解決策ではないと思います。
Raffi Khatchadourian、2012

@dlinsin:抽象クラスがinitを介して初期化されることになっていない場合、まさにそれが必要です。そのサブクラスは、どのスーパーメソッドを呼び出すかを知っていると考えられますが、これにより、 "new"または "alloc / init"による不注意な呼び出しが停止します。
gnasher729 2016年

21

抽象基本クラスを作成する代わりに、プロトコル(Javaインターフェースと同様)の使用を検討してください。これにより、一連のメソッドを定義し、プロトコルに準拠し、メソッドを実装するすべてのオブジェクトを受け入れることができます。たとえば、操作プロトコルを定義して、次のような機能を持つことができます。

- (void)performOperation:(id<Operation>)op
{
   // do something with operation
}

opは、操作プロトコルを実装する任意のオブジェクトです。

メソッドを定義するだけではなく、抽象基本クラスが必要な場合は、通常のObjective-Cクラスを作成して、インスタンス化されないようにすることができます。-(id)init関数をオーバーライドして、nilまたはassert(false)を返すようにします。これはあまりクリーンなソリューションではありませんが、Objective-Cは完全に動的であるため、抽象基本クラスに直接相当するものは実際にはありません。


私にとって、これは、少なくとも(C ++のように)「インターフェース」を意味することを意図している場合に、抽象クラスを使用する場合に適した方法のようです。このアプローチには隠れた欠点がありますか?
2011

1
@febeling、抽象クラス-少なくともJavaでは-は単なるインターフェースではありません。また、いくつか(またはほとんど)の動作も定義します。ただし、このアプローチはいくつかのケースで良いかもしれません。
Dan Rosenstark、2011

私のサブクラスがすべて共有する特定の機能を実装するには、基本クラスが必要です(重複を削除)。ただし、基本クラスが処理してはならない他のメソッド(抽象部分)のプロトコルも必要です。したがって、両方を使用する必要があります。サブクラスが適切に実装されていることを確認するのが難しい場合があります。
LightningStryk

19

このスレッドは古いもので、共有したいもののほとんどはすでにここにあります。

しかし、私のお気に入りの方法は言及されていません。また、私の知る限り、現在のClangにはネイティブサポートがないので、ここに行きます…

まず、何よりも(他の人がすでに指摘しているように)抽象クラスはObjective-Cでは非常に珍しいものです。通常、代わりに(委任を通じて)構成を使用します。これはおそらく、@dynamicCoreDataの導入に伴いObjC 2.0にIIRCが追加されたプロパティを除いて、そのような機能が言語/コンパイラにまだ存在しない理由です。

しかし、(状況を注意深く評価した後)委任(または一般的な構成)は問題の解決にあまり適していないという結論に達しました。ここでは、その方法を示します。

  1. 基本クラスにすべての抽象メソッドを実装します。
  2. その実装を行う[self doesNotRecognizeSelector:_cmd];
  3. …続いて__builtin_unreachable();、非voidメソッドに対して表示される警告を沈黙させ、「コントロールは戻りなしで非void関数の最後に到達しました」と通知します。
  4. 手順2.と3.をマクロで組み合わせるか、実装なしのカテゴリで-[NSObject doesNotRecognizeSelector:]使用__attribute__((__noreturn__))して注釈を付け、そのメソッドの元の実装を置き換えないようにし、そのカテゴリのヘッダーをプロジェクトのPCHに含めます。

ボイラープレートをできるだけ減らすことができるので、私は個人的にはマクロバージョンを好みます。

ここにあります:

// Definition:
#define D12_ABSTRACT_METHOD {\
 [self doesNotRecognizeSelector:_cmd]; \
 __builtin_unreachable(); \
}

// Usage (assuming we were Apple, implementing the abstract base class NSString):
@implementation NSString

#pragma mark - Abstract Primitives
- (unichar)characterAtIndex:(NSUInteger)index D12_ABSTRACT_METHOD
- (NSUInteger)length D12_ABSTRACT_METHOD
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange D12_ABSTRACT_METHOD

#pragma mark - Concrete Methods
- (NSString *)substringWithRange:(NSRange)aRange
{
    if (aRange.location + aRange.length >= [self length])
        [NSException raise:NSInvalidArgumentException format:@"Range %@ exceeds the length of %@ (%lu)", NSStringFromRange(aRange), [super description], (unsigned long)[self length]];

    unichar *buffer = (unichar *)malloc(aRange.length * sizeof(unichar));
    [self getCharacters:buffer range:aRange];

    return [[[NSString alloc] initWithCharactersNoCopy:buffer length:aRange.length freeWhenDone:YES] autorelease];
}
// and so forth…

@end

ご覧のとおり、マクロは抽象メソッドの完全な実装を提供し、必要なボイラープレートの量を最小限に抑えます。

さらに良い選択肢は クランチームロビー活動をすることです。機能をリクエストして、このケースにコンパイラー属性を提供するようことです。(これは、NSIncrementalStoreなどのサブクラス化するシナリオのコンパイル時診断も有効にするため、より良いです。)

この方法を選択する理由

  1. それは仕事を効率的に、そして幾分便利に行うことです。
  2. かなりわかりやすいです。(さて、それ__builtin_unreachable()は人々を驚かせるかもしれませんが、それも理解するのに十分簡単です。)
  3. アサーションマクロの1つに基づくアプローチとは異なり、他のコンパイラ警告またはエラーを生成せずにリリースビルドで取り除くことはできません。

その最後の点にはいくつかの説明が必要だと思います:

一部(ほとんど?)の人は、リリースビルドでアサーションを削除します。(私はその習慣に同意しませんが、それは別の話です...)必要なメソッドの実装に失敗する—しかし—は悪いひどい間違っている、そして基本的にあなたのプログラムの宇宙の終わりです。これは未定義であり、未定義の動作はこれまでで最悪の事態であるため、プログラムはこの点で正しく動作できません。したがって、新しい診断を生成せずにそれらの診断を取り除くことができることは完全に受け入れられないでしょう。

このようなプログラマエラーの適切なコンパイル時診断を取得できず、実行時にこれらのエラーを検出する必要があるのは十分に悪いことですが、リリースビルドでそれを解決できる場合は、なぜ抽象クラスを最初の場所?


これは私の新しいお気に入りのソリューションです__builtin_unreachable();。gemはこれを完璧に機能させます。マクロは自己文書化し、オブジェクトで欠落しているメソッドを呼び出した場合の動作と一致します。
Alex MDC 2013年

12

使用@propertyして動作する@dynamicこともできます。動的プロパティを宣言し、一致するメソッドの実装を指定しない場合でも、警告なしですべてがコンパイルされ、unrecognized selectorアクセスしようとすると実行時にエラーが発生します。これはを呼び出すのと基本的に同じです[self doesNotRecognizeSelector:_cmd]が、タイピングがはるかに少なくなります。


7

Xcode(clangなどを使用)では__attribute__((unavailable(...)))、抽象クラスにタグを付けるのに使用するので、使用しようとするとエラー/警告が表示されます。

これは、誤ってメソッドを使用することに対するある程度の保護を提供します。

基本クラス@interfaceタグで「抽象」メソッド:

- (void)myAbstractMethod:(id)param1 __attribute__((unavailable("You should always override this")));

これをさらに一歩進めて、マクロを作成します。

#define UnavailableMacro(msg) __attribute__((unavailable(msg)))

これにより、次のことが可能になります。

- (void)myAbstractMethod:(id)param1 UnavailableMacro(@"You should always override this");

先ほど言ったように、これは実際のコンパイラ保護ではありませんが、抽象メソッドをサポートしない言語を使用するのとほぼ同じです。


1
理解できませんでした。私は私の基本クラスであなたの提案を適用-initの方法となりましたXcodeは私が継承したクラスのインスタンスを作成することはできませんし、コンパイル時にエラーが発生し使用できなく...。詳しく説明していただけますか?
anonim

-initサブクラスにメソッドがあることを確認してください。
Richard Stelling、

2
これは、そのような属性でマークされたメソッドを呼び出そうとするたびにエラーをトリガーするNS_UNAVAILABLEと同じです。抽象クラスでどのように使用できるかわかりません。
ベンシンクレア

7

質問に対する回答は、すでに与えられた回答の下のコメントに散在しています。だから、私はここで要約し、単純化しています。

オプション1:プロトコル

実装のない抽象クラスを作成する場合は、「プロトコル」を使用します。プロトコルを継承するクラスは、プロトコルにメソッドを実装する必要があります。

@protocol ProtocolName
// list of methods and properties
@end

オプション2:テンプレートメソッドパターン

「テンプレートメソッドパターン」のような部分的な実装で抽象クラスを作成したい場合、これが解決策です。 Objective-C-テンプレートメソッドパターン?


6

別の選択肢

AbstractクラスのクラスとAssertまたはExceptionを確認してください。

@implementation Orange
- (instancetype)init
{
    self = [super init];
    NSAssert([self class] != [Orange class], @"This is an abstract class");
    if (self) {
    }
    return self;
}
@end

これにより、オーバーライドする必要がなくなります init


nilを返すだけで効率的でしょうか?それとも例外/その他のエラーがスローされるのでしょうか?
user2277872 14

まあ、これはプログラマーがクラスを直接使用することをキャッチするためのものです。
bigkm 2014

5

(関連する提案の詳細)

プログラマーに「子から呼び出さない」ことを知らせ、完全にオーバーライドする方法を望んでいました(私の場合、拡張されていない場合でも、親に代わってデフォルトの機能をいくつか提供しています)。

typedef void override_void;
typedef id override_id;

@implementation myBaseClass

// some limited default behavior (undesired by subclasses)
- (override_void) doSomething;
- (override_id) makeSomeObject;

// some internally required default behavior
- (void) doesSomethingImportant;

@end

利点は、プログラマーが宣言の「オーバーライド」を参照し、呼び出してはならないことを認識すること[super ..]です。

確かに、これには個々の戻り値の型を定義する必要がありますが、視覚的なヒントとして十分に機能し、サブクラス定義で「override_」の部分を簡単に使用することはできません。

もちろん、拡張機能がオプションの場合でも、クラスはデフォルトの実装を持つことができます。しかし、他の回答が言うように、抽象(仮想)クラスの場合のように、適切な場合はランタイム例外を実装します。

コメントやドキュメントを調べたり、想定したりするのではなく、このようなコンパイラヒントを組み込み、スーパーの実装を事前または事後呼び出しするのに最適な場合のヒントを用意しておくとよいでしょう。

ヒントの例


4

他の言語で抽象的なインスタンス化違反をキャッチするコンパイラーに慣れている場合、Objective-Cの動作は期待外れです。

レイトバインディング言語として、Objective-Cがクラスが本当に抽象的であるかどうかを静的に判断できないことは明らかです(実行時に関数を追加している可能性があります...)。実行時にエラーをスローするのではなく、コンパイラーのフラットアウトによって抽象クラスのインスタンス化が防止されることを望みます。

以下は、イニシャライザを非表示にするいくつかの手法を使用して、このタイプの静的チェックを取得するために使用しているパターンです。

//
//  Base.h
#define UNAVAILABLE __attribute__((unavailable("Default initializer not available.")));

@protocol MyProtocol <NSObject>
-(void) dependentFunction;
@end

@interface Base : NSObject {
    @protected
    __weak id<MyProtocol> _protocolHelper; // Weak to prevent retain cycles!
}

- (instancetype) init UNAVAILABLE; // Prevent the user from calling this
- (void) doStuffUsingDependentFunction;
@end

//
//  Base.m
#import "Base.h"

// We know that Base has a hidden initializer method.
// Declare it here for readability.
@interface Base (Private)
- (instancetype)initFromDerived;
@end

@implementation Base
- (instancetype)initFromDerived {
    // It is unlikely that this becomes incorrect, but assert
    // just in case.
    NSAssert(![self isMemberOfClass:[Base class]],
             @"To be called only from derived classes!");
    self = [super init];
    return self;
}

- (void) doStuffUsingDependentFunction {
    [_protocolHelper dependentFunction]; // Use it
}
@end

//
//  Derived.h
#import "Base.h"

@interface Derived : Base
-(instancetype) initDerived; // We cannot use init here :(
@end

//
//  Derived.m
#import "Derived.h"

// We know that Base has a hidden initializer method.
// Declare it here.
@interface Base (Private)
- (instancetype) initFromDerived;
@end

// Privately inherit protocol
@interface Derived () <MyProtocol>
@end

@implementation Derived
-(instancetype) initDerived {
    self= [super initFromDerived];
    if (self) {
        self->_protocolHelper= self;
    }
    return self;
}

// Implement the missing function
-(void)dependentFunction {
}
@end

3

おそらくこの種の状況は開発時にのみ発生するはずなので、これでうまくいくかもしれません:

- (id)myMethodWithVar:(id)var {
   NSAssert(NO, @"You most override myMethodWithVar:");
   return nil;
}

3

@Yarによって提案されたメソッドを使用できます(一部変更を加えます)。

#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
#define setMustOverride() NSLog(@"%@ - method not implemented", NSStringFromClass([self class])); mustOverride()

ここでは、次のようなメッセージが表示されます。

<Date> ProjectName[7921:1967092] <Class where method not implemented> - method not implemented
<Date> ProjectName[7921:1967092] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[<Base class (if inherited or same if not> <Method name>] must be overridden in a subclass/category'

またはアサーション:

NSAssert(![self respondsToSelector:@selector(<MethodName>)], @"Not implemented");

この場合、次のようになります。

<Date> ProjectName[7926:1967491] *** Assertion failure in -[<Class Name> <Method name>], /Users/kirill/Documents/Projects/root/<ProjectName> Services/Classes/ViewControllers/YourClass:53

また、プロトコルやその他のソリューションを使用することもできますが、これは最も簡単な方法の1つです。


2

Cocoaは抽象と呼ばれるものを提供していません。実行時にのみチェックされるクラスアブストラクトを作成できますが、コンパイル時にはチェックされません。


1

私は通常、抽象化したいクラスのinitメソッドを無効にします:

- (instancetype)__unavailable init; // This is an abstract class.

これにより、そのクラスでinitを呼び出すたびにコンパイル時にエラーが発生します。次に、他のすべてにクラスメソッドを使用します。

Objective-Cには、抽象クラスを宣言するための組み込みの方法はありません。


しかし、その抽象クラスの派生クラスから[super init]を呼び出すとエラーが発生します。それを解決するには?
Sahil Doshi

@SahilDoshiこの方法を使用することの欠点だと思います。クラスのインスタンス化を許可したくなく、そのクラスから何も継承しない場合に使用します。
mahoukov

はい、理解しました。しかし、あなたのソリューションは、誰かがinitを呼び出さないようにするシングルトンクラスのようなものを作成するのに役立ちます。抽象クラスの場合の私の知識によれば、いくつかのクラスはそれを継承します。それ以外の場合、抽象クラスの使用はどうですか。
Sahil Doshi

0

@dotToStringのコメントを適用して@redfoodが提案する内容を少し変更すると、実際にはInstagramのIGListKitがソリューションを採用しています。

  1. 基本(抽象)クラスで定義しても意味がないすべてのメソッドのプロトコルを作成します。つまり、子の特定の実装が必要です。
  2. このプロトコルを実装しない基本(抽象)クラスを作成します。このクラスには、共通の実装を持つことに意味のある他のメソッドを追加できます。
  3. プロジェクトのどこにいても、子AbstractClassが何らかの方法で入力または出力される必要がある場合は、AbstractClass<Protocol>代わりに入力します。

AbstractClassはを実装していないためProtocolAbstractClass<Protocol>インスタンスを作成する唯一の方法は、サブクラス化することです。AbstractClassだけでは、プロジェクト内のどこでも使用することができない、それが抽象的になります。

もちろん、これは、賢明ではない開発者が単にを参照する新しいメソッドを追加することを妨げるものAbstractClassではありません。

実際の例:IGListKitにIGListSectionController、プロトコルを実装しない基本クラスIGListSectionTypeがありますが、そのクラスのインスタンスを必要とするすべてのメソッドは、実際にタイプを要求しIGListSectionController<IGListSectionType>ます。したがって、タイプのオブジェクトをIGListSectionControllerフレームワークで役立つものに使用する方法はありません。


0

実際、Objective-Cには抽象クラスはありませんが、プロトコルを使用して同じ効果を得ることができます。これがサンプルです:

CustomProtocol.h

#import <Foundation/Foundation.h>

@protocol CustomProtocol <NSObject>
@required
- (void)methodA;
@optional
- (void)methodB;
@end

TestProtocol.h

#import <Foundation/Foundation.h>
#import "CustomProtocol.h"

@interface TestProtocol : NSObject <CustomProtocol>

@end

TestProtocol.m

#import "TestProtocol.h"

@implementation TestProtocol

- (void)methodA
{
  NSLog(@"methodA...");
}

- (void)methodB
{
  NSLog(@"methodB...");
}
@end

0

抽象クラスを作成する簡単な例

// Declare a protocol
@protocol AbcProtocol <NSObject>

-(void)fnOne;
-(void)fnTwo;

@optional

-(void)fnThree;

@end

// Abstract class
@interface AbstractAbc : NSObject<AbcProtocol>

@end

@implementation AbstractAbc

-(id)init{
    self = [super init];
    if (self) {
    }
    return self;
}

-(void)fnOne{
// Code
}

-(void)fnTwo{
// Code
}

@end

// Implementation class
@interface ImpAbc : AbstractAbc

@end

@implementation ImpAbc

-(id)init{
    self = [super init];
    if (self) {
    }
    return self;
}

// You may override it    
-(void)fnOne{
// Code
}
// You may override it
-(void)fnTwo{
// Code
}

-(void)fnThree{
// Code
}

@end

-3

デリゲートを作成できませんか?

デリゲートは、定義する必要がある関数を言うという意味では抽象基本クラスに似ていますが、実際には定義しません。

次に、デリゲート(つまり、抽象クラス)を実装するたびに、動作を定義する必要があるオプション関数と必須関数についてコンパイラーから警告を受けます。

これは抽象基底クラスのように思えます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.