公式の話によると、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
型があるとは思わない。プライベートプロパティ/メソッドを公に宣言する以外に、とにかくこれを達成する方法はあるのだろうか?
これを解決する1つの方法は、サブクラスのクラス拡張でプロパティを再宣言し、@dynamic
ステートメント。これにより、コンパイラはそのプロパティのオーバーライド実装を作成しません。のようなもの:
@interface SuperClass ()
@property (nonatomic, strong) id someProperty;
@end
....
@interface SubClass ()
@property (nonatomic, strong) id someProperty;
@end
@implementation SubClass
@dynamic someProperty;
@end
これは明らかに、非公開の宣言を複製するため理想的ではありません。しかし、状況によっては非常に便利で役立つので、この重複に伴う危険とパブリックインターフェイスでのプロパティの公開の危険性をケースバイケースで評価します。
別の方法-UIGestureRecognizerのAppleで使用)は、「SomeClass + Protected.h」のように「private」または「protected」として明示的に名前が付けられた個別のカテゴリヘッダーファイルでプロパティを宣言することですそうすれば、他のプログラマーはファイルをインポートしてはならないことを知っていますが、継承元のコードを制御しない場合、それはオプションではありません。
これは、基本クラスとサブクラスの両方の実装ファイルに含めるクラス拡張(カテゴリではない)を使用することで可能です。
クラス拡張はカテゴリと同様に定義されますが、カテゴリ名はありません:
@interface MyClass ()
クラス拡張では、バッキングivarを合成できるプロパティを宣言できます(XCode> 4.4 ivarの自動合成もここで機能します)。
拡張クラスでは、プロパティをオーバーライド/リファイン(読み取り専用を読み取り書き込みなどに変更)し、実装ファイルに「表示」されるプロパティとメソッドを追加できます(ただし、プロパティとメソッドは実際にはプライベートではなく、セレクターから呼び出されます)。
このために別のヘッダーファイル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
を使用して、コンパイラーが保護されたクラス拡張を含めるようにします。
他の答えは正しいですが、追加したいと思います...
プライベート、プロテクト、パブリックareは、たとえばvariablesとして利用可能です:
@interface MyClass : NSObject {
@private
int varA;
@protected
int varB;
@public
int varC;
}
@end
唯一の選択肢は、ヘッダーファイルでパブリックとして宣言することです。少なくともメソッドの分離を維持したい場合は、カテゴリを作成し、そこにすべての保護されたメソッドと属性を設定できますが、最終的にはすべてが公開されます。
#import "MyClass.h"
@interface MyClass (Protected)
- (void) protectedMethods;
@end
プロパティを可視化するための良い答えはありますが、これらの答えのどれにも非常に明確に対処されたメソッドを公開することはありません。カテゴリを使用してサブクラスにプライベートメソッドを正常に公開した方法を次に示します。
SomeSuperClass.m:
@implementation SomeSuperClass
-(void)somePrivateMethod:(NSString*)someArgument {
...
}
SomeChildClass.h
@interface SomeChildClass : SomeSuperClass
SomeChildClass.m
@interface SomeSuperClass (exposePrivateMethod)
-(void)somePrivateMethod:(NSString*)someArgument;
@end
@implementation SomeChildClass
-(void)doSomething {
[super somePrivateMethod:@"argument"];
}
@end
クラス拡張子を付けて.hファイルを作成するだけです。これを.mファイルにインポートします。ちなみに、これはカプセル化を壊さずにプライベートメンバーをテストするのに最適な方法です(プライベートメソッドをテストする必要はありません:))。
// MyClassProtectedMembers.h
@interface MyClass()
@property (nonatomic, strong) MyPrivateObject *privateObject;
- (void) privateMethod;
@end
//////////////////
#import "MyClassProtectedMembers.h"
@implementation MyClass
// implement privateMethod here and any setters or getters with computed values
@end
ここにアイデアの要点があります: https://Gist.github.com/philosopherdog/6461536b99ef73a5c32a
それは、プライベートとパブリックの本当の区別さえないからです。コンパイラーは、特定のメソッドまたはインスタンス変数が欠落しているインターフェースについて警告する場合がありますが、プログラムは引き続き機能します。