3

私の調査によると、 @privateディレクティブには実際の使用法がないことが示されています。そのため、何かが欠けている必要があり、専門家に協力してもらう必要があります:-)

CarクラスとSportsCarクラスの 2 つのクラスがあるとします。 SportsCarCarのサブクラスです。

Carクラスは次のとおりです。

@interface Car : NSObject {
    NSString *make;
    NSString *model;

    @private
    int numberOfBackSeatPassengers;  // I'm making this a private iVar cause I'm just gonna
                // say that all Sportscars will be 2-seaters and therefore shouldn't
                // be able to set/get the number of back-seat passengers
}

@property (nonatomic, strong) NSString *make, *model;

// Now here's my first issue: if I also make "numberOfBackSeatPassengers" an @property 
// then it seems like all subclasses of this Car class *WILL* be able to access it as 
// well - even though I declared it as @private - but I'll do this anyway to make my point:

@property int numberOfBackSeatPassengers;

@end

実装は次のようになります。

@implementation Car

@synthesize make, model, numberOfBackSeatPassengers;

@end

これがスポーツカークラスです。

#import "Car.h"

@interface Sportscar : Car

@property int turboEngineSize;

@end

そしてその実装:

#import "Sportscar.h"

@implementation Sportscar

@synthesize turboEngineSize;

@end

「メイン」にはこれがあります:

    Car *car1 = [[Car alloc] init];
    [car1 setMake:@"Chevy"];
    [car1 setModel:@"Impala"];
    [car1 setNumberOfBackSeatPassengers:3];

    Sportscar *sports1 = [[Sportscar alloc] init];
    [sports1 setMake:@"Audi"];
    [sports1 setModel:@"tt"];
    [sports1 setNumberOfBackSeatPassengers:3];

明らかに、iVar が@privateとして宣言されていても、スポーツカーにNumberOfBackSeatPassengerを設定できますが、それは「Car.h」で@propertyにしたためです。これは、合成されたゲッターとセッターがインスタンスであることを意味します。メソッド。これにより、 Carのすべてのサブクラスで使用できます。

もう 1 つのオプションは、「Car.h」で numberOfBackSeatPassengers を @property として宣言ず、単純な iVar として保持し、代わりに「Car.m」の @implementationで手動でセッターとゲッターを作成することでした。このような:

-(void) setNumberOfBackSeatPassengers:(int)numPassgeners {
    numberOfBackSeatPassengers = numPassgeners;
}

-(int)numberOfBackSeatPassengers {
    return numberOfBackSeatPassengers;
}

これにより、numberOfBackSeatPassengers のゲッターとセッターが" Car.m " 内でのみ使用可能になります (これにより、"プライベート" になると思います)。 「Car.m」 さらに、これが本当のポイントです。このようにすることは、「Car.h」の @private ディレクティブが実際にはまったく機能しないことを意味します。つまり、「Car.h」に戻って、そこにある「 @private」ディレクティブを取り出すことができます。また、numberOfBackSeatPassengers手動セッターとゲッターは、現在とまったく同じように機能し、おそらくプライベートです。 「@private」で得られる ? それは実際にどのように機能しますか?

誰かがこれに本当の光を当てることができますか?

(そして、はい、 「Car.m」ファイルの @interface セクションで Car クラスを拡張できることを知っています-カテゴリを介して、または最初にnumberOfBackSeatPassengersを読み取り専用プロパティにしてから、それを readwrite に変更するなど-しかし、これらすべて「@private」を機能させるための回避策または「ハック」のように思えます.@privateが実際に単独でどのように機能するのかわかりません。

================================================== ===

編集 - 以下のアロスのコメントに応えて:

1) を使用して、サブクラスがその親クラスのヘッダーで宣言されていないメソッドを理論的に呼び出すことができるという aroth の発言は絶対に正しいperformSelectorです。私は「理論的に」と言いますが、私の場合は正しく機能していないためです: if - 「メイン」で - 私は呼び出します

[sportscar1 performSelector:@selector(setNumberOfBackSeatPassengers:)];

numberOfBackSeatPassengers次に、この方法でメソッドを呼び出すときに、数値を引数として明示的に渡すことができないため、ジャンク番号が挿入されます。

(質問: これを回避する方法はありますか?)

2) arothは、クラスの setter と getter をSportscar単にオーバーライドして、これらのオーバーライド メソッドで 0 にリセットしたり、エラーを発生させたりすることもできます。しかし、これは非常に実用的な解決策であり、この特定の問題は、実際にはすべきことをしていないように見えるというより大きな問題に対処していないように感じます.CarnumberOfBackSeatPassengers@private

3) ロジックを再設計して、クラスFourDoorCarと別 のクラスを持ちTwoDoorCar、それから構築を続けることは興味深いオプションですが、Objective-C の構文が自分のプログラミング ロジックと自分のやり方に「強制」されているように感じます。私のまさにプロジェクトを構築すること - そしてこれはかなりの負担のように感じます. 多分私は間違っていて、これを使いすぎているのかもしれませんが、どちらにしても@private、約束されているように見えないという理由だけで、これはすべて起こりました...? 気分が悪い。

結局のところ、私は同じ質問に何度も戻って@privateきます。それにはどのような利点がありますか、何を「購入」しますか? iVar を非公開にしたい場合は、「.m」ファイルで宣言するだけでよく、最初からヘッダー ファイルで宣言する必要はないようです。私はこれについて正しいですか?または、ヘッダーでiVarを として宣言したいが、ヘッダーでセッターとゲッターを宣言したくないインスタンスがまだあります@private-したがって、それらはサブクラスで明示的に利用できません-そしてそれをすべて作ります検出?

これについて実際の例を考えることができますか? 何らかの形で私に利益をもたらす (「.m」ではなく)@private ヘッダーのように宣言したいある種の Car プロパティはありますか? 良い例だと思いnumberOfBackSeatPassengersましたが、実際のコードで実際にどのように機能するかわかりません...

================================================== =======================

編集 #2 - @aroth との対話を続ける :-)

@aroth - すべての iVar をヘッダーで宣言し、一部をヘッダーに、一部を実装に分割しない方がはるかに優れている/より組織化されていることに絶対に同意します。それは混乱を引き起こし、私はそのアプローチが本当に嫌いです。(元の質問で、質問に対処するために実装やカテゴリのアプローチを使用したくないことに注意しました。) -また、プロパティは常に iVar によってバックアップされる必要はありません。

-クラスを適切に設計することに関しては、もちろんそれが優れたプログラミングの鍵であることに同意します。車/スポーツカーの例は、質問に文脈を与えるためにその場で作り上げたものであり、その設計のメリット/欠陥を考慮して時間をかけませんでした. しかし、私たちがあなたのアプローチを採用した場合(これは確かに非常に論理的だと思われます)、Carクラス、FourDoorCarサブクラス、TwoDoorCarサブクラスなどを使用すると、多くの問題を解決できると思いますが、それでも可能性は非常に高いです遅かれ早かれ、クラスの 1 つに @private iVar が必要で、それを処理する別のサブクラスを作成したくないという状況に遭遇するでしょう。つまり、この議論のために、これが起こると仮定しましょう。

したがって、可能であれば、 @privateとして持つことが理にかなっている Car クラスの特定の iVar を考えて、その使用方法をコードで示し、その範囲と制限について説明したいと思います。

私は、Car だけに持たせたい、そしてそのサブクラスのどれも継承すべきではない Car のいくつかのプロパティの実世界の例を常に考えようとしています。私は本当にうまくいくと思っnumBackSeatPassengersていました-そして、私たちの議論の目的のために、それはまだできますが、私は別のものを作ってそれを呼びますphantomIVar:-)

など:

@interface Car : NSObject {

    @private
    //int numberOfBackSeatPassengers;  
    int phantomIVar;

}

@property (nonatomic, strong) NSString *make, *model;


@end

実装は次のようになります。

@implementation Car

@synthesize make, model;

-(void) setPhantomIVar:(int)i {
    phantomIVar = i;
}

-(int)phantomIVar {
    return phantomIVar;
}

@end

これは、私たちが始めたところに戻ってきました:-)

少なくとも私はそう感じています。

つまり、@private宣言が私たちを買収しているように見える唯一のことは、読みやすさです。そのため、ヘッダーを見ている人は誰でもphantomIVar、それが Car の iVar であることがわかり、それが非公開であることを理解できます。それでおしまい。

ただし、機能面では、あまり機能していないように見えました。@private解放された前に配置するのphantomIVarとは異なり、ヘッダーにセッター/ゲッターを記述し、それらを Car クラス オブジェクトにのみアクセス可能にし、Car のサブクラスにはアクセスできないようにします。いいえ、それはわかりません@private。プライバシーを確​​保するには、実装ファイルに移動して、そこにセッターとゲッターを記述する必要があります。そして最終的に、Objective-C にはプライベート メソッドのようなものはありません。オブジェクトで。C. それらはすべて公開されています。

ああ、これが正しいかどうか教えてください。そうでない場合は、どこで間違ったのか教えてください。

どうもありがとう :-)

4

2 に答える 2

0

readonlyクラス自体でプロパティを宣言することも、クラスでのみプロパティをCar再宣言することもできます。readonlySportsCar

また、@privateプロパティとは何の関係もありません。ivar 自体のスコープを変更するだけです。

于 2012-09-15T12:31:07.120 に答える
0

これにより、numberOfBackSeatPassengers のゲッターとセッターが「Car.m」内でのみ使用可能になります。

違います。これらのメソッドは、ヘッダー ファイルで宣言するかどうかに関係なく、Carのすべてのインスタンスおよび を拡張するすべてのオブジェクトのすべてのインスタンスに引き続き存在します。コンパイラはそれらを一般に公開されているものとして処理せず、それらを直接呼び出そうとすると文句を言いますが、 を使用するだけCarで、 のサブクラスでゲッターとセッターを呼び出すことができます。 CarperformSelector:

いずれにせよ、それをサポートする ivar で@property使用@privateする意味がない場合 (また、それをサポートする明示的な ivar を使用しても意味がありません。使用時に自動的に作成されます@synthesize。ただし、それは別のトピックです)。 . SportsCarifを拡張Carし、後部座席の乗客が記録されることを決して許可しないことをお勧めしSportsCarますゼロ以外の値を設定しようとするとエラーになります。

このプロパティはすべてのインスタンスに適用されるわけではないため、もう 1 つのオプションCarは、基本クラスから完全に除外することです。たとえば、 を持っていてCar、その から派生した と を持っていてTwoDoorCar、から派生してFourDoorCarいるSportsCar可能性がありますTwoDoorCar。この場合、すべての 4 ドア車は後部座席に乗客を収容できる必要があるためnumberOfBackSeatPassengers、 の公共財産として宣言できます。FourDoorCar

元の質問に戻ると、ivar を使用@privateすると、その ivar の可視性のみに影響します。ivar を使用するメソッドには影響しません。そのため、 のサブクラスはivar 自体Carを見ることができません。numberOfBackSeatPassengersしかし、パブリックな getter/setter を作成したので、もちろんサブクラスはそれらを確認し、それらを使用して ivar の値を変更できます。

編集

更新された質問に簡単に答えるには:

  1. はい、NSInvocationを使用して、プリミティブ パラメーターを必要とするメソッドを動的に呼び出すことができます。または、ここで説明したアプローチを使用することもできます。これはさらに簡単です: Objective-C と SEL/IMP の使用。または、 のNSNumber代わりに を使用してから を使用することもできintますperformSelector:withObject:

  2. この場合、あなたが何を@privateすべきかわかりません。を使用すると何を@privateすべきだと思いますか?

  3. これは構文とはあまり関係がなく、オブジェクト指向設計の原則と関係があると思います。一部の車に後部座席がない場合、CarスーパークラスにnumberOfBackseatPassengersプロパティを与えることは、オブジェクト指向設計としては適切ではありません。これを行うと、オブジェクト タイプのすべてのインスタンスに実際には適用されないフィールドがオブジェクトに与えられます。そして、それをやり始めると、例で説明したのとまったく同じ種類の問題に遭遇します。スーパークラスの目的は、そのすべての派生型に共通する機能を含めることです。一部の派生型にのみ共通の機能がある場合、それは通常、設計上の問題です。いずれにしても、Objective-C の構文やセマンティクスとは何の関係もありません。

@privateが得られるかというと、たとえば、クラスの編成を簡素化するのはどうですか? はい、実装ファイルで ivar を宣言して同様の効果を達成できますが、ヘッダーですべての ivar を宣言するのと同じくらい便利ですか? かなり複雑なプロジェクトで、一部のivar のみがヘッダーで宣言され、残りが実装ファイルにある場合、他の開発者はあなたのコードを簡単にたどることができますか?

Jonathan が指摘したすべての理由から、ヘッダーで宣言されたすべてのivarが公開され@privateます。したがって、これらのアクセス修飾子はおそらく、何よりもまずこの問題を解決するために存在します。 @protected

ユースケースに関しては、getter/setter を持つプロパティはおそらく最良の例ではありません。ゲッター/セッターの目的は、ほぼ常に、プロパティ値を変更/クエリするためのパブリック インターフェイスを提供することです。Objective-C で説明されているように、合成されたプロパティをサポートするために、任意のスコープで ivar を明示的に宣言する必要はありません。

より良い例はIBOutlet's. XCode/Interface Builder がそれらを見つけられるように、これらをヘッダーで宣言する必要がありますが、クラス実装の外部に、または (通常) クラスのサブクラスにさえ公開したくありません。したがって、ヘッダーでそれらを宣言し、通常、これらの ivar に getter/setter メソッドを追加しません。

編集 2

@privateどこが理にかなっているのかの具体例として、次のようなものはどうですか:

@interface Car : NSObject {

    @private
    DataRecorder* blackBoxRecorder;

}

@property (nonatomic, strong) NSString *make, *model;

@end

提案された規制では、道路上のすべての車に組み込みのブラック ボックス/データ レコーダーを含めることが義務付けられる可能性があることはわかっています。したがって、すべてCarに 1 つが必要であり、 のサブクラスがCarを改ざんできないようにする必要がありblackBoxRecorderます。

この場合、setter メソッドを定義しても意味がありません。DataRecorderパブリック ゲッターを提供するか、サブクラスがデータのログに使用できるの周りにパブリック ラッパー API を提供する場合があります。のようなもの-(void) logEventWithName:(NSString*)name andValue:(NSNumber*)value;。そのため、サブクラスはDataRecorderAPI を介して を使用できますが、バッキング ivar 自体をいじって、必須のブラック ボックス/データ レコーダーの動作を無効にしたり変更したりすることはできません。

しかし、いずれにせよ、はい、私はあなたの分析に概ね同意します。@privateほとんどの場合、コードの可読性/保守性に影響します。Objective-C がオブジェクト指向プログラミング言語として成功するためには、それが存在する必要があります (すべての ivar がデフォルトで公開されていて、それを変更する方法がなかった場合、言語は完全に混乱するでしょう)。純粋に機能的な観点はあまりありません。それはより論理的/組織的なツールです。データの隠蔽を支援し、すべての ivar をヘッダー ファイルに保持できるようにします。それだけです。

于 2012-09-15T12:39:08.023 に答える