35

いくつかの公式の話によると、Objective-Cのクラスは、ヘッダー内のパブリックメソッドとプロパティのみを公開する必要があります。

@interface MyClass : NSObject

@property (nonatomic, strong) MyPublicObject *publicObject;

- (void)publicMethod;

@end

プライベートメソッド/プロパティは、.mファイルのクラス拡張子に保持する必要があります。

@interface MyClass()

@property (nonatomic, strong) MyPrivateObject *privateObject;

- (void) privateMethod;

@end

protectedプライベートであるがサブクラスからアクセスできるタイプはないと思います。プライベートプロパティ/メソッドを公に宣言する以外に、これを達成する方法はありますか?

4

7 に答える 7

35

これを解決する 1 つの方法は、サブクラスのクラス拡張でプロパティを再宣言し、@dynamicステートメントを追加して、コンパイラがそのプロパティのオーバーライド実装を作成しないようにすることです。次のようなものです:

@interface SuperClass ()

@property (nonatomic, strong) id someProperty;

@end

....


@interface SubClass ()

@property (nonatomic, strong) id someProperty;

@end

@implementation SubClass

@dynamic someProperty;

@end

これは明らかに理想的ではありません。なぜなら、プライベートに見える宣言を複製するからです。ただし、状況によっては非常に便利で役立つため、この重複に伴う危険性とパブリック インターフェイスでプロパティを公開することの危険性をケースバイケースで評価することをお勧めします。

Apple が UIGestureRecognizer で使用する別の方法は、"private" または "protected" として明示的に指定された別のカテゴリ ヘッダー ファイル ("SomeClass+Protected.h" など) でプロパティを宣言することです。そうすれば、他のプログラマーはファイルをインポートしてはならないことを知ることができます。ただし、継承元のコードを制御しない場合、それはオプションではありません。

于 2012-09-28T04:46:07.480 に答える
16

これは、基本クラスとサブクラスの両方の実装ファイルに含めるクラス拡張 (カテゴリではない) を使用することで可能になります。

クラス拡張はカテゴリと同様に定義されますが、カテゴリ名はありません:

@interface MyClass ()

クラス拡張では、バッキング ivar を合成できるプロパティを宣言できます (XCode > 4.4 ivar の自動合成もここで機能します)。

拡張クラスでは、プロパティをオーバーライド/調整し (readonly を readwrite に変更するなど)、実装ファイルに「表示」されるプロパティとメソッドを追加できます (ただし、プロパティとメソッドは実際には非公開ではないことに注意してください。まだセレクターによって呼び出されます)。

他の人は、これに別のヘッダー ファイル MyClass_protected.h を使用することを提案していますが、これは次の#ifdefように使用してメイン ヘッダー ファイルで行うこともできます。

例:

BaseClass.h

@interface BaseClass : NSObject

// foo is readonly for consumers of the class
@property (nonatomic, readonly) NSString *foo;

@end


#ifdef BaseClass_protected

// this is the class extension, where you define 
// the "protected" properties and methods of the class

@interface BaseClass ()

// foo is now readwrite
@property (nonatomic, readwrite) NSString *foo;

// bar is visible to implementation of subclasses
@property (nonatomic, readwrite) int bar;

-(void)baz;

@end

#endif

BaseClass.m

// this will import BaseClass.h
// with BaseClass_protected defined,
// so it will also get the protected class extension

#define BaseClass_protected
#import "BaseClass.h"

@implementation BaseClass

-(void)baz {
    self.foo = @"test";
    self.bar = 123;
}

@end

ChildClass.h

// this will import BaseClass.h without the class extension

#import "BaseClass.h"

@interface ChildClass : BaseClass

-(void)test;

@end

ChildClass.m

// this will implicitly import BaseClass.h from ChildClass.h,
// with BaseClass_protected defined,
// so it will also get the protected class extension

#define BaseClass_protected 
#import "ChildClass.h"

@implementation ChildClass

-(void)test {
    self.foo = @"test";
    self.bar = 123;

    [self baz];
}

@end

を呼び出すと#import、基本的に .h ファイルをインポート先にコピーして貼り付けます。がある場合、その名前の が設定されている#ifdef場合にのみコードが含まれます。#define

.h ファイルでは定義を設定しないため、この .h をインポートするクラスは保護されたクラスの拡張を認識しません。基本クラスとサブクラスの .m ファイルでは、使用する#define前に使用#importして、コンパイラが保護されたクラス拡張を含めるようにします。

于 2014-02-06T15:53:20.630 に答える
8

他の答えは正しいですが、追加したいと思います...

プライベート、プロテクト、およびパブリック、インスタンス変数として次のように使用できます。

@interface MyClass : NSObject {
@private
  int varA;

@protected
  int varB;

@public
  int varC;
}

@end
于 2012-09-28T04:49:16.937 に答える
1

唯一の選択肢は、ヘッダーファイルでパブリックとして宣言することです。少なくともメソッドの分離を維持したい場合は、カテゴリを作成し、保護されたすべてのメソッドと属性をそこに含めることができますが、最終的にはすべてが公開されたままになります。

#import "MyClass.h"

@interface MyClass (Protected)

- (void) protectedMethods;

@end
于 2012-09-28T04:36:09.557 に答える
0

それは、プライベートとパブリックの間に本当の区別さえないからです。インターフェイスに特定のメソッドまたはインスタンス変数がないことをコンパイラが警告する場合がありますが、プログラムは引き続き機能します。

于 2012-09-28T04:38:17.777 に答える