0

重複の可能性:
Objective-Cがプライベートメソッドをサポートしないのはなぜですか?
プライベートメソッド宣言Objective-C

私がやりたいのは、Objective Cの特定のメソッドの実装をプライベートにすることです(Java用語を使用するため)。つまり、メソッドがサブクラスによってオーバーライドされることを心配せずに、クラスが必要に応じて呼び出すことができるメソッドです。おそらく、この用語は「上書きされないように安全」です。

例として、オブジェクトには複数のinitメソッドがあり、一般的な初期化を行うためのプライベート初期化メソッドがあります。

- (void) initWithFile:(NSString *)file
{
    if ((self = [super init]) != nil) {
        self.file = file;
        self.url = nil;
        [self initialize]
    }
    return self;
}

- (void) initWithURL:(NSURL *)url
{
    if ((self = [super init]) != nil) {
        self.url = url;
        self.file = nil;
        [self initialize]
    }
    return self;
}

- (void) initialize
{
    ...
}

これは機能し、初期化が.hファイルで宣言されていない場合、他のクラスから呼び出されないように「保護」されます。

サブクラスで問題が発生します(次のクラスが前の例のサブクラスであると想定しています)。

- (void) initWithFile:(NSString *)file
{
    if ((self = [super initWithFile:file]) != nil) {
        [self initialize];   // my local initializations
    }
    return self;
}

- (void) initialize
{
    ... // my private stuff
}

問題は、この例では、サブクラスのinitializeメソッドが呼び出されることはなく、サブクラスのinitializeメソッドが基本クラスのメソッドをオーバーライドしているため、サブクラスのinitializeメソッドが2回呼び出されることです。

サブクラスで[superinitialize]を呼び出し、サブクラスのinitメソッドでの呼び出しを省略できることはわかっていますが、そのためには、サブクラスが基本クラスの内部を知っている必要があります。サブクラスは基本クラスのソースにアクセスする必要はなく、.hインターフェイスファイルのみが必要であるため、動作を変更するという望ましくない副作用を引き起こすことなく、初期化メソッドを実装できると考える理由があります。基本クラスの。

初期化関数でプロパティが設定されていないことに直接関係する問題があり、サブクラスによって取得されていることがわかっただけで、これをデバッグするのに時間がかかったため、これを求めています。そもそもこれを防ぎたい。

4

6 に答える 6

1

objCのメソッドにprivateまたはfinalキーワードはありません

ヘッダーにそれらを含めないでください...
多分それらになんらかの名前を付けてください__XYかそこら...


ところで:Javaではprivateはオーバーライドに反対していません、それは単なる別の「副作用」です。キーワードfinalはオーバーライドに反対です

于 2012-12-22T18:19:42.520 に答える
1

Objective-Cにはそのようなことはありません。ただし、できることは、メソッドを定義しないことです。

Appleはこれをこのように行うことがよくあります。

@interface SomeClass ()
- (void)_someNonPublicMethod;
@end

これはカテゴリであり、クラスで何を定義する必要があるかを簡単に示しています。これは、.mファイルで行います。

すでに述べた他のいくつかの回答と同様に、クラスダンプを使用してクラスが実装するメソッドを見つけることができ、プライベートであってもそれらをオーバーライドできます。

于 2012-12-22T18:30:34.887 に答える
1

これに対するかなり確実な解決策の1つは、静的cメソッドを使用して初期化を行うことです。すべての有効なcは有効なobjective-cです。

したがって、たとえば、CustomSuperclass.h:

#import <Foundation/Foundation.h>
@interface CustomSuperclass : NSObject
@property (nonatomic) NSUInteger integer;
@end

CustomSuperclass.m:

#import "CustomSuperclass.h"
@implementation CustomSuperclass
@synthesize integer = _integer;
static void initializeInstance(CustomSuperclass *self) {
    self.integer = 9;
}
-(id)init{
    if ((self = [super init])){
        initializeInstance(self);
    }
    return self;
}
@end

CustomSubclass.h:

#import "CustomSuperclass.h"
@interface CustomSubclass : CustomSuperclass
@end

CustomSubclass.m:

#import "CustomSubclass.h"
@implementation CustomSubclass
// Obviously we would normally use (CustomSubclass *self), but we're proving a point with identical method signatures.
void initializeInstance(CustomSuperclass *self) {
    self.integer = 10;
}
-(id)init{
    if ((self = [super init])){
        // We won't call this classes `initializeInstance()` function.
        // We want to see if this one overrides it's superclasses version.
    }
    return self;
}
@end

次のようなコードを実行すると、次のようになります。

CustomSuperclass *instanceOfSuperclass = [[CustomSuperclass alloc] init];
NSLog(@"superclass integer:%i", instanceOfSuperclass.integer);

CustomSubclass *instanceOfSubclass = [[CustomSubclass alloc] init];
NSLog(@"subclass integer:%i", instanceOfSubclass.integer);

次のように表示されます。

superclass integer:9
subclass integer:9

サブクラスのバージョンがinitializeInstance()サブクラスによって使用されたものである場合(呼び出し時[super init])、サブクラスの整数は10になります。これは、スーパークラスのinitializeInstance()メソッドがサブクラスのバージョンによって(少なくともスーパークラスのスコープ内で)オーバーライドされなかったことを証明します。 。

コメントに応じて編集します。

私はこの回答をその解決策とともに投稿しました。それが問題を解決するための最良の方法であると思うからではなく、質問の仕様を満たしているからです。問題は、基本的に、「動的言語を静的な方法で動作させて、通常の命名/初期化規則に従う必要がないようにするにはどうすればよいですか?」です。

この状況を処理する最良の方法は、各初期化子が呼び出す指定された初期化子をクラスに持たせることです。好き:

/* Designated initializer */
-(id)initWithFile:(NSString *)file andURL:(NSURL *)url{
    if ((self = [super init])){
        _url = url;
        _file = file;
        // Initialization of new instance code here.
    }
    return self;
}
-(id)initWithFile:(NSString *)file{
    return [self initWithFile:file andURL:nil];
}
-(id)initWithURL:(NSURL *)url{
    return [self initWithFile:nil andURL:url];
}

指定された初期化子は、ほとんどすべての場合に機能します。UIViewIBは、インスタンス化されたコードが呼び出される/呼び出されるはずの場所を呼び出すため、サブクラスUIViewにとってinitWithCoder:UIView少し面倒ですinitWithFrame:[super initWith...]これは、コードの重複を減らすために、の呼び出し後に終了する一般的なメソッドが記述されている可能性がある状況です。

一般的な初期化メソッドの名前をオーバーライドする際に発生する問題が発生する可能性があります。これは、ほとんどの人にとって、隠すことによる少しのセキュリティが機能する場所です。たとえば、次のようになります。

-(void)initialize_CustomUIView{
    // Common init tasks
}
-(id)initWithFrame:(CGRect)frame{
    if ((self = [super initWithFrame:frame])){
        [self initialize_CustomUIView];
    }
    return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
    if ((self = [super initWithCoder:aDecoder])){
        [self initialize_CustomUIView];
    }
    return self;
}
-(id)init{
    if ((self = [super init])){
        [self initialize_CustomUIView];
    }
    return self;
}

しかし、繰り返しになりますが、一般的な初期化メソッドをオーバーライドしないようにする方法、つまりstatic void initializeInstance()解決策を具体的に尋ねました。

于 2012-12-22T20:15:28.083 に答える
0

このページを見てくださいそれはプライベートメソッドを含むベストオブジェクティブcコードプラクティスについてです:)

http://ironwolf.dangerousgames.com/blog/archives/913

于 2012-12-22T18:24:47.233 に答える
0

これは.mファイルにある必要があります:

@interface MyClass()

- (void) privateMethod1;

@end

@implementation MyClass

@end
于 2012-12-22T18:30:38.470 に答える
0

@implementationで定義されたメソッド、または名前のないカテゴリで宣言されたメソッドは表示されませんが、実行することはできます。ただし、実際の問題は、インスタンスごとに初期化メソッドが必要なことですが、次のようになります。

  • このメソッドをプライベートにしたいが、このクラスのユーザーにそれが存在することを知らせ、彼らはそれをオーバーライドする必要があります。どのように?将来あなたはこれを気に入らないでしょう。
  • このメソッドをオーバーライドして、別のオーバーライドされたメソッドから呼び出します。これにより、子メソッドが二重に実行されます。

作成しようとしている規則は意味がありません。各initializeメソッドに異なる名前を付け、initからそれぞれを呼び出す必要があります。

于 2012-12-22T19:28:44.643 に答える