361

私は Objective-C のプログラミングを始めたばかりで、Java のバックグラウンドを持っているため、Objective-C プログラムを作成している人々がプライベート メソッドをどのように扱っているのか疑問に思っています。

いくつかの規則や習慣が存在する可能性があることを理解しており、この質問は、Objective-C でプライベート メソッドを処理するために人々が使用する最良の手法の集合体として考えています。

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


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

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

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

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

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

4

12 に答える 12

438

他の人がすでに言ったように、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

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

于 2009-03-16T19:37:02.270 に答える
54

ランタイムがどの実装を使用するかを判断できる場合、Objective-C には実際には「プライベート メソッド」はありません。しかし、文書化されたインターフェースの一部ではないメソッドがないと言っているわけではありません。それらの方法については、カテゴリは問題ないと思います。ポイント2のように.mファイルの先頭に置くのではなく@interface、独自の.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 つのパスを実行し始めました。これは、「プライベート」メソッドを完全に宣言することを回避できることを意味し、それらが呼び出しサイトの上または下にあるかどうかにかかわらず、コンパイラによって検出されます。

于 2008-10-06T05:49:46.810 に答える
37

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

于 2008-10-05T22:03:16.753 に答える
23

ブロックでプライベート メソッドを定義すること@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 メソッドの数を最小限に抑えながら、非常に高速であり、より高度な制御を提供できます。

于 2013-05-26T09:45:48.773 に答える
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
于 2009-03-16T18:54:15.213 に答える
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 つです。このソリューションが他の場所で議論されているのを見たことがないので、これを行うのがばかげている場合はお知らせください。

于 2012-06-17T04:35:28.227 に答える
3

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

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

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

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

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

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

于 2010-11-19T16:21:21.230 に答える
2

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

//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"
于 2013-02-14T03:08:12.023 に答える
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
于 2010-08-03T23:48:02.477 に答える
1

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

于 2009-03-16T18:33:08.430 に答える
1

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

于 2011-08-08T07:31:36.810 に答える