すべての要素が型であるNSMutableArray
インスタンスを作成できますか?SomeClass
11 に答える
まだ誰もこれをここに上げていないので、私がやります!
Tthis は現在、Objective-C で公式にサポートされています。Xcode 7 以降では、次の構文を使用できます。
NSArray<MyClass *> *myArray = @[[MyClass new], [MyClass new]];
ノート
これらはコンパイラの警告のみであり、技術的には配列に任意のオブジェクトを挿入できることに注意することが重要です。すべての警告をビルドを妨げるエラーに変えるスクリプトが利用可能です。
これは、厳密に型付けされた言語 (C++ や Java など) から、Python、Ruby、または Objective-C などのより弱くまたは動的に型付けされた言語に移行する人々にとって比較的一般的な質問です。Objective-C では、ほとんどのオブジェクトはNSObject
(type id
) から継承し (残りは、 などの他のルート クラスから継承し、NSProxy
type にすることもできますid
)、任意のメッセージを任意のオブジェクトに送信できます。もちろん、認識できないインスタンスにメッセージを送信すると、実行時エラーが発生する可能性があります (また、コンパイラの警告も発生します)。適切な -W フラグ付き)。送信したメッセージにインスタンスが応答する限り、それがどのクラスに属しているかは気にしなくてもかまいません。これはしばしば「ダックタイピング」と呼ばれます。「アヒルのように鳴く [つまり、セレクターに応答する] 場合、それはアヒルです [つまり、メッセージを処理できます。それがどのクラスであるかは誰にも関係ありません]」.
メソッドを使用して、実行時にインスタンスがセレクターに応答するかどうかをテストできます-(BOOL)respondsToSelector:(SEL)selector
。配列内のすべてのインスタンスでメソッドを呼び出したいが、すべてのインスタンスがメッセージを処理できるかどうかわからないと仮定します (したがって、NSArray
'sだけを使用することはできません。次の-[NSArray makeObjectsPerformSelector:]
ようなものが機能します。
for(id o in myArray) {
if([o respondsToSelector:@selector(myMethod)]) {
[o myMethod];
}
}
呼び出したいメソッドを実装するインスタンスのソース コードを制御する場合、より一般的な方法は、@protocol
それらのメソッドを含む を定義し、問題のクラスが宣言でそのプロトコルを実装することを宣言することです。この使用法では、a@protocol
は Java インターフェイスまたは C++ 抽象基本クラスに類似しています。その後、各メソッドへの応答ではなく、プロトコル全体への適合性をテストできます。前の例では、大きな違いはありませんが、複数のメソッドを呼び出す場合は、物事が単純化される可能性があります。例は次のようになります。
for(id o in myArray) {
if([o conformsToProtocol:@protocol(MyProtocol)]) {
[o myMethod];
}
}
と仮定してMyProtocol
宣言しmyMethod
ます。この 2 番目のアプローチは、最初のアプローチよりもコードの意図を明確にするため、好まれます。
多くの場合、これらのアプローチの 1 つを使用すると、配列内のすべてのオブジェクトが特定の型であるかどうかを気にする必要がなくなります。それでも気にする場合、標準の動的言語アプローチは単体テスト、単体テスト、単体テストです。この要件のリグレッションは、(おそらく回復不可能な) ランタイム エラー (コンパイル時ではない) を生成するため、クラッシャーを野放しにしないように、動作を検証するためのテスト カバレッジが必要です。この場合、配列を変更する操作を実行してから、配列内のすべてのインスタンスが特定のクラスに属していることを確認します。適切なテスト カバレッジがあれば、インスタンスの ID を検証するための追加のランタイム オーバーヘッドも必要ありません。単体テストのカバレッジは良好ですよね。
コンパイル時の静的型チェックを許可するメソッドを使用してカテゴリを作成することはできます-addSomeClass:
(そのため、そのメソッドを介して別のクラスであることがわかっているオブジェクトを追加しようとすると、コンパイラが通知する可能性があります) が、それを強制する実際の方法はありません。配列には、特定のクラスのオブジェクトのみが含まれます。
一般に、Objective-C ではそのような制約は必要ないようです。経験豊富な Cocoa プログラマーがその機能を望んでいると聞いたことはないと思います。他の言語で考えている他の言語のプログラマーのように見えるのは、それらの言語だけです。配列内の特定のクラスのオブジェクトのみが必要な場合は、そのクラスのオブジェクトのみをそこに貼り付けます。コードが適切に動作することをテストしたい場合は、テストしてください。
NSMutableArray
タイプセーフを強制するためにサブクラス化できます。
NSMutableArray
クラス clusterであるため、サブクラス化は簡単ではありません。NSArray
そのクラス内の配列から呼び出しを継承して転送することになりました。その結果、サブクラス化が容易なクラスが呼び出さConcreteMutableArray
れます。これが私が思いついたものです:
更新:クラス クラスタのサブクラス化については、Mike Ash のこのブログ投稿をご覧ください。
これらのファイルをプロジェクトに含めてから、マクロを使用して必要なタイプを生成します。
MyArrayTypes.h
CUSTOM_ARRAY_INTERFACE(NSString)
CUSTOM_ARRAY_INTERFACE(User)
MyArrayTypes.m
CUSTOM_ARRAY_IMPLEMENTATION(NSString)
CUSTOM_ARRAY_IMPLEMENTATION(User)
使用法:
NSStringArray* strings = [NSStringArray array];
[strings add:@"Hello"];
NSString* str = [strings get:0];
[strings add:[User new]]; //compiler error
User* user = [strings get:0]; //compiler error
他の考え
NSArray
シリアライゼーション/デシリアライゼーションをサポートするために継承します好みに応じて、次のような一般的なメソッドをオーバーライド/非表示にすることができます
- (void) addObject:(id)anObject
この Github プロジェクトはまさにその機能を実装しています。
<>
その後、C# の場合と同じようにブラケットを使用できます。
彼らの例から:
NSArray<MyClass>* classArray = [NSArray array];
NSString *name = [classArray lastObject].name; // No cast needed
可能な方法は NSArray をサブクラス化することですが、Apple はそうしないことを推奨しています。型指定された NSArray の実際の必要性について 2 回考える方が簡単です。
NSArray のクラスクラスターの性質に関する問題を回避するために、NSArray オブジェクトをバッキング ivar として使用する NSArray サブクラスを作成しました。オブジェクトの追加を承認または拒否するにはブロックが必要です。
AddBlock
NSString オブジェクトのみを許可するには、 asを定義できます
^BOOL(id element) {
return [element isKindOfClass:[NSString class]];
}
FailBlock
要素がテストに失敗した場合、何をすべきかを決定するために a を定義できます— フィルタリングのために正常に失敗するか、それを別の配列に追加するか — これはデフォルトです — 例外を発生させます。
VSBlockTestedObjectArray.h
#import <Foundation/Foundation.h>
typedef BOOL(^AddBlock)(id element);
typedef void(^FailBlock)(id element);
@interface VSBlockTestedObjectArray : NSMutableArray
@property (nonatomic, copy, readonly) AddBlock testBlock;
@property (nonatomic, copy, readonly) FailBlock failBlock;
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock Capacity:(NSUInteger)capacity;
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock;
-(id)initWithTestBlock:(AddBlock)testBlock;
@end
VSBlockTestedObjectArray.m
#import "VSBlockTestedObjectArray.h"
@interface VSBlockTestedObjectArray ()
@property (nonatomic, retain) NSMutableArray *realArray;
-(void)errorWhileInitializing:(SEL)selector;
@end
@implementation VSBlockTestedObjectArray
@synthesize testBlock = _testBlock;
@synthesize failBlock = _failBlock;
@synthesize realArray = _realArray;
-(id)initWithCapacity:(NSUInteger)capacity
{
if (self = [super init]) {
_realArray = [[NSMutableArray alloc] initWithCapacity:capacity];
}
return self;
}
-(id)initWithTestBlock:(AddBlock)testBlock
FailBlock:(FailBlock)failBlock
Capacity:(NSUInteger)capacity
{
self = [self initWithCapacity:capacity];
if (self) {
_testBlock = [testBlock copy];
_failBlock = [failBlock copy];
}
return self;
}
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock
{
return [self initWithTestBlock:testBlock FailBlock:failBlock Capacity:0];
}
-(id)initWithTestBlock:(AddBlock)testBlock
{
return [self initWithTestBlock:testBlock FailBlock:^(id element) {
[NSException raise:@"NotSupportedElement" format:@"%@ faild the test and can't be add to this VSBlockTestedObjectArray", element];
} Capacity:0];
}
- (void)dealloc {
[_failBlock release];
[_testBlock release];
self.realArray = nil;
[super dealloc];
}
- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
if(self.testBlock(anObject))
[self.realArray insertObject:anObject atIndex:index];
else
self.failBlock(anObject);
}
- (void) removeObjectAtIndex:(NSUInteger)index
{
[self.realArray removeObjectAtIndex:index];
}
-(NSUInteger)count
{
return [self.realArray count];
}
- (id) objectAtIndex:(NSUInteger)index
{
return [self.realArray objectAtIndex:index];
}
-(void)errorWhileInitializing:(SEL)selector
{
[NSException raise:@"NotSupportedInstantiation" format:@"not supported %@", NSStringFromSelector(selector)];
}
- (id)initWithArray:(NSArray *)anArray { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfFile:(NSString *)aPath{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfURL:(NSURL *)aURL{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(id)firstObj, ... { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { [self errorWhileInitializing:_cmd]; return nil;}
@end
次のように使用します。
VSBlockTestedObjectArray *stringArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
return [element isKindOfClass:[NSString class]];
} FailBlock:^(id element) {
NSLog(@"%@ can't be added, didn't pass the test. It is not an object of class NSString", element);
}];
VSBlockTestedObjectArray *numberArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
return [element isKindOfClass:[NSNumber class]];
} FailBlock:^(id element) {
NSLog(@"%@ can't be added, didn't pass the test. It is not an object of class NSNumber", element);
}];
[stringArray addObject:@"test"];
[stringArray addObject:@"test1"];
[stringArray addObject:[NSNumber numberWithInt:9]];
[stringArray addObject:@"test2"];
[stringArray addObject:@"test3"];
[numberArray addObject:@"test"];
[numberArray addObject:@"test1"];
[numberArray addObject:[NSNumber numberWithInt:9]];
[numberArray addObject:@"test2"];
[numberArray addObject:@"test3"];
NSLog(@"%@", stringArray);
NSLog(@"%@", numberArray);
これは単なるサンプル コードであり、実際のアプリケーションでは使用されませんでした。そのためには、おそらく NSArray メソッドを実装する必要があります。