Objective-Cでクラスのプライベートメソッドを定義する最良の方法


355

私はObjective-Cのプログラミングを始めたばかりで、Javaの経験があるため、Objective-Cプログラムを書く人々がプライベートメソッドをどのように扱うのか疑問に思います。

私はいくつかの慣習や習慣があるかもしれないことを理解しており、Objective-Cでプライベートメソッドを扱うために人々が使用する最高のテクニックのアグリゲーターとしてこの質問について考えます。

投稿するときは、アプローチの引数を含めてください。なぜそれが良いのですか?(あなたが知っている)どの欠点があり、どのようにそれらに対処しますか?


これまでの私の調査結果についても。

MyClass.mファイルで定義されたカテゴリ [eg MyClass(Private)] を使用して、プライベートメソッドをグループ化することができます。

このアプローチには2つの問題があります。

  1. Xcode(およびコンパイラ?)は、対応する@implementationブロックのプライベートカテゴリのすべてのメソッドを定義しているかどうかをチェックしません
  2. MyClass.mファイルの先頭にプライベートカテゴリを宣言する@interfaceを配置する必要があります。そうしないと、Xcodeは「自分はメッセージ「privateFoo」に応答しない可能性があります」などのメッセージを表示します。

最初の問題は、空のカテゴリ [MyClass()]などで回避できます。
二つ目は私をとても悩ませます。ファイルの終わり近くにプライベートメソッドを実装(および定義)したいのですが。それが可能かどうかわかりません。


1
:人々はこの質問の興味深い見つけるかもしれないstackoverflow.com/questions/2158660/...
bbum

4
プライベートメソッドの宣言を省略しないのはなぜですか?
ma11hew28

回答:


435

他の人がすでに言ったように、Objective-Cのプライベートメソッドのようなものはありません。ただし、Objective-C 2.0(Mac OS X Leopard、iPhone OS 2.0以降)以降では、Class Extension@interface MyClass ()と呼ばれる空の名前(つまり)のカテゴリを作成できます。クラス拡張のユニークな点は、メソッドの実装はパブリックメソッドと同じでなければならないことです。だから私はこのように私のクラスを構築します:@implementation MyClass

.hファイル:

@interface MyClass {
    // My Instance Variables
}

- (void)myPublicMethod;

@end

そして.mファイルで:

@interface MyClass()

- (void)myPrivateMethod;

@end

@implementation MyClass

- (void)myPublicMethod {
    // Implementation goes here
}

- (void)myPrivateMethod {
    // Implementation goes here
}

@end

このアプローチの最大の利点は、(場合によっては任意の)パブリック/プライベートの区別ではなく、機能ごとにメソッド実装をグループ化できることです。


8
そして、それは「MYClassは '-myPrivateMethod-に応答しない可能性があります。例外/エラーではありません。
Özgür

2
これは実際にはAppleの定型コードに現れ始めています。++
Chris Trahey

75
LLVM 4コンパイラ以降では、これを行う必要すらありません。それらを実装内で定義するだけで、クラス拡張に置く必要はありません。
Abizern

1
@Comptrolが言及する警告を受け取った場合、それはそれを呼び出す別のメソッドの上ではなく、以下のメソッドを定義したためです(Andyの回答を参照)-これらの警告は危険にさらされても無視されます。私はこのミスを犯し、私はこのような呼び出しにネストされるまで、コンパイラは大丈夫を通じて混乱:if (bSizeDifference && [self isSizeDifferenceSignificant:fWidthCombined])...次にfWidthCombinedと常に0が通過来ていた
Wienke

6
@Wienke注文について心配する必要はもうありません。LLVMの最近のバージョンでは、呼び出された場所の下に表示されていても、そのメソッドを見つけます。
Steve Waddicor 2013年

52

Objective-Cには、どの実装を使用するかをランタイムが判断できる場合、実際には「プライベートメソッド」はありません。しかし、それはドキュメント化されたインターフェースの一部ではないメソッドがないということではありません。これらの方法については、カテゴリは問題ないと思います。@interfaceポイント2のように.mファイルの先頭にを置くのではなく、独自の.hファイルに入れます。私が従う規則(および他の場所で見たように、Xcodeが自動的にサポートするようになったので、これはAppleの規則だと思います)は、そのようなファイルにクラスとカテゴリの後に+を付けて名前を付ける@interface GLObject (PrivateMethods)ことGLObject+PrivateMethods.hです。ヘッダーファイルを提供する理由は、ユニットテストクラスにインポートできるようにするためです:-)。

ちなみに、.mファイルの末尾近くにあるメソッドの実装/定義に関する限り、.mファイルの下部にあるカテゴリを実装することで、カテゴリでそれを行うことができます。

@implementation GLObject(PrivateMethods)
- (void)secretFeature;
@end

またはクラス拡張(「空のカテゴリ」と呼ぶもの)を使用して、これらのメソッドを最後に定義するだけです。Objective-Cメソッドは、実装で任意の順序で定義および使用できるため、「プライベート」メソッドをファイルの最後に置くのを止めることは何もありません。

クラス拡張を使用しても、別のヘッダー(GLObject+Extension.h)を作成することがよくあり、必要に応じてこれらのメソッドを使用して、「友達」または「保護された」可視性を模倣できます。

この回答はもともと書かれていたため、clangコンパイラはObjective-Cメソッドに対して2つのパスを実行し始めました。これは、「プライベート」メソッドを完全に宣言すること、およびそれらがコンパイラーによって検出される呼び出しサイトの上または下のどちらにあるかを回避できることを意味します。


37

私はObjective-Cの専門家ではありませんが、個人的にはクラスの実装でメソッドを定義するだけです。確かに、それはそれを呼び出すメソッドの前に(上で)定義する必要がありますが、実行するのに必要な作業量は確実に最小です。


4
このソリューションには、コンパイラの警告を回避するためだけに余分なプログラム構造を追加する必要がないという利点があります。
Jan Hettich

1
私もこれを行う傾向がありますが、Objective-Cのエキスパートでもありません。専門家にとって、この方法を行わない理由はありますか(メソッドの順序付けの問題は別として)?
Eric Smith

2
メソッドの順序は軽微な問題のようですが、コードの読みやすさに変換すると、特にチームで作業するときに非常に重要な問題になる可能性があります。
borisdiakur 2012

17
メソッドの順序は重要ではなくなりました。LLVMの最近のバージョンでは、メソッドが実装される順序は関係ありません。したがって、最初に宣言する必要なしに、注文に自分自身を合わせることができます。
Steve Waddicor 2013年

@justinからのこの応答も参照してください
lagweezle

19

@implementationブロックでプライベートメソッドを定義することは、ほとんどの目的にとって理想的です。Clangは@implementation、宣言の順序に関係なく、内でこれらを参照します。クラスの継続(別名クラス拡張)または名前付きカテゴリでそれらを宣言する必要はありません。

場合によっては、クラスの継続でメソッドを宣言する必要があります(たとえば、クラスの継続との間のセレクターを使用する場合@implementation)。

static 関数は、特に機密性の高い、または速度が重要なプライベートメソッドに非常に適しています。

プレフィックスの命名規則は、プライベートメソッドを誤ってオーバーライドするのを防ぐのに役立ちます(クラス名はプレフィックスとして安全だと思います)。

名前付きカテゴリ(など@interface MONObject (PrivateStuff))は、ロード時に名前が競合する可能性があるため、特に良いアイデアではありません。これらは本当に、フレンドメソッドまたは保護されたメソッドにのみ役立ちます(これは非常にまれな選択肢です)。不完全なカテゴリの実装について警告されるようにするには、実際に実装する必要があります。

@implementation MONObject (PrivateStuff)
...HERE...
@end

これは少し注釈が付いたチートシートです:

MONObject.h

@interface MONObject : NSObject

// public declaration required for clients' visibility/use.
@property (nonatomic, assign, readwrite) bool publicBool;

// public declaration required for clients' visibility/use.
- (void)publicMethod;

@end

MONObject.m

@interface MONObject ()
@property (nonatomic, assign, readwrite) bool privateBool;

// you can use a convention where the class name prefix is reserved
// for private methods this can reduce accidental overriding:
- (void)MONObject_privateMethod;

@end

// The potentially good thing about functions is that they are truly
// inaccessible; They may not be overridden, accidentally used,
// looked up via the objc runtime, and will often be eliminated from
// backtraces. Unlike methods, they can also be inlined. If unused
// (e.g. diagnostic omitted in release) or every use is inlined,
// they may be removed from the binary:
static void PrivateMethod(MONObject * pObject) {
    pObject.privateBool = true;
}

@implementation MONObject
{
    bool anIvar;
}

static void AnotherPrivateMethod(MONObject * pObject) {
    if (0 == pObject) {
        assert(0 && "invalid parameter");
        return;
    }

    // if declared in the @implementation scope, you *could* access the
    // private ivars directly (although you should rarely do this):
    pObject->anIvar = true;
}

- (void)publicMethod
{
    // declared below -- but clang can see its declaration in this
    // translation:
    [self privateMethod];
}

// no declaration required.
- (void)privateMethod
{
}

- (void)MONObject_privateMethod
{
}

@end

明白ではないかもしれない別のアプローチ:C ++型は、非常に高速であり、はるかに高度な制御を提供すると同時に、エクスポートおよびロードされるobjcメソッドの数を最小限に抑えることができます。


1
完全なクラス名をメソッド名の接頭辞として使用するための+1!アンダースコアや独自のTLAよりもはるかに安全です。(プライベートメソッドが別のプロジェクトで使用するライブラリにあり、1年か2年前にその名前を既に使用したことを忘れた場合はどうなりますか?)
big_m

14

インスタンスへのポインタを取得する実装の下または上に静的関数を定義してみることができます。インスタンス変数にアクセスできます。

//.h file
@interface MyClass : Object
{
    int test;
}
- (void) someMethod: anArg;

@end


//.m file    
@implementation MyClass

static void somePrivateMethod (MyClass *myClass, id anArg)
{
    fprintf (stderr, "MyClass (%d) was passed %p", myClass->test, anArg);
}


- (void) someMethod: (id) anArg
{
    somePrivateMethod (self, anArg);
}

@end

1
Appleは、独自の使用のために先頭にアンダースコアを付けた名前を予約しました。
GeorgSchölly2009年

1
そして、もしあなたがAppleのフレームワークを使わないとしたらどうでしょう?私は頻繁にLinuxでは、WindowsとMac OS X.私はとにかくおそらくマックOS X上での使用にそれを行うのObjective-Cのコードほとんどの人を考慮し、それを取り除い上の実際のIビルドで、アップルのフレームワークなしでのObjective-Cのコードを開発
dreamlax

3
これは.mファイルの本当にプライベートな方法だと思います。@interface ... @ endブロックにメソッドのプライベートを配置できないため、他のクラスカテゴリメソッドは実際にはプライベートではありません。
David.Chu.ca

どうしてそうするか?メソッド定義の最初に単に「-」を追加すると、パラメーターとして渡さずに「self」にアクセスすることになります。
2014年

1
@Guy:メソッドはリフレクションで検出できるため、非公開ではありません。
dreamlax 2014年

3

あなたはブロックを使うことができますか?

@implementation MyClass

id (^createTheObject)() = ^(){ return [[NSObject alloc] init];};

NSInteger (^addEm)(NSInteger, NSInteger) =
^(NSInteger a, NSInteger b)
{
    return a + b;
};

//public methods, etc.

- (NSObject) thePublicOne
{
    return createTheObject();
}

@end

これは古い質問であることは承知していますが、この質問に対する答えを探していたときに最初に見つけた質問の1つです。この解決策が他のどこかで議論されたのを見たことがないので、これを行うことについて何か愚かなことがあったら教えてください。


1
ここで行ったのは、グローバルブロックタイプの変数を作成することです。これは関数よりも優れているわけではありません(宣言されていないため、本当にプライベートでさえありませんstatic)。ただし、私は(initメソッドから)プライベートivarにブロックを割り当てる実験を行っています。これは、JavaScriptのようなスタイルで、静的関数からは不可能であるプライベートivarへのアクセスも許可します。私がどちらを好むかまだわかりません。
big_m 2015年

3

Objective Cのすべてのオブジェクトは、performSelector:メソッドを保持するNSObjectプロトコルに準拠しています。以前は、パブリックレベルで公開する必要のない「ヘルパーまたはプライベート」メソッドを作成する方法も探していました。オーバーヘッドのないプライベートメソッドを作成し、ヘッダーファイルでそれを定義する必要がない場合は、これを試してください...

以下のコードと同様のシグネチャでメソッドを定義します...

-(void)myHelperMethod: (id) sender{
     // code here...
}

次に、メソッドを参照する必要がある場合は、セレクタとして呼び出すだけです...

[self performSelector:@selector(myHelperMethod:)];

このコード行により、作成したメソッドが呼び出され、ヘッダーファイルで定義されていないことに関する煩わしい警告は表示されません。


6
この方法では、3番目のパラメーターを渡す方法はありません。
Li Fumin、2011

2

@interface先頭のブロックを回避したい場合は、プライベート宣言を常に別のファイルに置くことができますがMyClassPrivate.h、実装が煩雑ではありません。

MyClass.h

interface MyClass : NSObject {
 @private
  BOOL publicIvar_;
  BOOL privateIvar_;
}

@property (nonatomic, assign) BOOL publicIvar;
//any other public methods. etc
@end

MyClassPrivate.h

@interface MyClass ()

@property (nonatomic, assign) BOOL privateIvar;
//any other private methods etc.
@end

MyClass.m

#import "MyClass.h"
#import "MyClassPrivate.h"
@implementation MyClass

@synthesize privateIvar = privateIvar_;
@synthesize publicIvar = publicIvar_;

@end

2

ここで言及していないもう1つのこと-Xcodeは、名前に「_private」を含む.hファイルをサポートしています。MyClassクラスがあるとします。MyClass.mとMyClass.hがあり、MyClass_private.hもあるとします。Xcodeはこれを認識し、アシスタントエディターの「カウンターパート」のリストに含めます。

//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"

1

問題#2を回避する方法はありません。これは、Cコンパイラ(およびObjective-Cコンパイラ)が機能する方法に過ぎません。XCodeエディターを使用する場合、関数のポップアップにより、ファイル内の@interfaceおよび@implementationブロック内を簡単にナビゲートできます。


1

プライベートメソッドの不在の利点があります。非表示にするロジックを別のクラスに移動して、デリゲートとして使用できます。この場合、デリゲートオブジェクトをプライベートとしてマークすると、外部からは見えなくなります。ロジックを別のクラス(おそらく複数)に移動すると、プロジェクトの設計が改善されます。クラスが単純になり、メソッドが適切な名前のクラスにグループ化されます。


0

他の人々が言っ​​たように、@implementationブロックでプライベートメソッドを定義することはほとんどの目的で問題ありません。

コード編成のトピックについて-私pragma mark privateはXcodeでのナビゲーションを容易にするためにそれらをまとめておくのが好きです

@implementation MyClass 
// .. public methods

# pragma mark private 
// ...

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