Mac / iPhoneプログラミングとObjective-Cは初めてです。C#とJavaには、「ジェネリックス」と呼ばれるコレクションクラスがあり、そのメンバーは宣言された型のみにすることができます。たとえば、C#では
Dictionary<int, MyCustomObject>
MyCustomObjectタイプの整数と値のキーのみを含めることができます。Objective-Cにも同様のメカニズムはありますか?
Mac / iPhoneプログラミングとObjective-Cは初めてです。C#とJavaには、「ジェネリックス」と呼ばれるコレクションクラスがあり、そのメンバーは宣言された型のみにすることができます。たとえば、C#では
Dictionary<int, MyCustomObject>
MyCustomObjectタイプの整数と値のキーのみを含めることができます。Objective-Cにも同様のメカニズムはありますか?
回答:
Xcode 7では、AppleはObjective-Cに「Lightweight Generics」を導入しました。Objective-Cでは、型の不一致があるとコンパイラ警告が生成されます。
NSArray<NSString*>* arr = @[@"str"];
NSString* string = [arr objectAtIndex:0];
NSNumber* number = [arr objectAtIndex:0]; // Warning: Incompatible pointer types initializing 'NSNumber *' with an expression of type 'NSString *'
Swiftコードでは、コンパイラエラーが発生します。
var str: String = arr[0]
var num: Int = arr[0] //Error 'String' is not convertible to 'Int'
軽量ジェネリックは、NSArray、NSDictionary、NSSetで使用することを目的としていますが、独自のクラスに追加することもできます。
@interface GenericsTest<__covariant T> : NSObject
-(void)genericMethod:(T)object;
@end
@implementation GenericsTest
-(void)genericMethod:(id)object {}
@end
Objective-Cは、以前のようにコンパイラの警告で動作します。
GenericsTest<NSString*>* test = [GenericsTest new];
[test genericMethod:@"string"];
[test genericMethod:@1]; // Warning: Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'
しかし、SwiftはGeneric情報を完全に無視します。(Swift 3以降ではもう当てはまりません。)
var test = GenericsTest<String>() //Error: Cannot specialize non-generic type 'GenericsTest'
これらのFoundationコレクションクラス以外に、Objective-Cの軽量ジェネリックはSwiftによって無視されます。軽量ジェネリックを使用する他のタイプは、パラメータ化されていないかのようにSwiftにインポートされます。
MyClass <Foo: id<Bar>>
、制約を指定した場合、Swiftコードは、値が制約のタイプであると想定します。これにより、何かを操作できます。ただし、特殊化されたサブクラスのMyClass
特殊化されたタイプは無視されます(実質的にジェネリックと同じように見えますMyClass
)。github.com/bgerstle/LightweightGenericsExampleを
この答えは時代遅れですが、歴史的な価値のために残っています。Xcode 7以降、2015年6月8日のConnorの回答はより正確です。
いいえ、独自のカスタムコレクションクラスでC ++テンプレートを使用する場合を除いて、Objective-Cにはジェネリックはありません(強くお勧めしません)。
Objective-Cには機能として動的型付けがあります。つまり、すべてのオブジェクトがメッセージを受信できるため、ランタイムはオブジェクトの型を気にしません。組み込みのコレクションにオブジェクトを追加すると、それらはtypeであるかのように扱われid
ます。ただし、心配しないでください。通常のように、これらのオブジェクトにメッセージを送信してください。これは正常に機能します(もちろん、コレクション内の1つ以上のオブジェクトが、送信しているメッセージに応答しない場合を除きます)。
ジェネリックは、強力な静的型付き言語であるため、JavaやC#などの言語で必要です。Objective-Cの動的タイピング機能とはまったく異なる球技。
いいえ。ただし、わかりやすくするために、保存するオブジェクトのタイプでコメントを付けることができます。最近、Java 1.4で何かを記述する必要があるときに、これが数回行われたことを確認しました)例:
NSMutableArray* /*<TypeA>*/ arrayName = ....
または
NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...
Objective-Cにはジェネリックはありません。
配列は、オブジェクトの順序付けられたコレクションです。Cocoaは、NSArray、NSMutableArray(NSArrayのサブクラス)、およびNSPointerArrayのいくつかの配列クラスを提供します。
AppleはXCode 7でObjCにジェネリックを追加しました:
@property NSArray<NSDate *>* dates;
- (NSArray<NSDate *> *)datesBeforeDate:(NSDate *)date;
- (void)addDatesParsedFromTimestamps:(NSArray<NSString *> *)timestamps;
こちらをご覧ください:https : //developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6-ID61
これはXcode 7でリリースされました(ついに!)
Objective Cコードでは、これはコンパイル時のチェックにすぎないことに注意してください。コレクションに間違った型を入力したり、型付きのプロパティに割り当てたりするだけで、実行時エラーは発生しません。
宣言:
@interface FooClass <T> : NSObject
@property (nonatomic) T prop;
@end
使用する:
FooClass<NSString *> *foo = [[FooClass alloc] init];
NSArray<FooClass<NSString *> *> *fooAry = [NSArray array];
それらに注意してください*
。
ジェネリックNSArrayは、をサブクラス化しNSArray
、提供されたすべてのメソッドをより制限的なメソッドで再定義することで実現できます。例えば、
- (id)objectAtIndex:(NSUInteger)index
で再定義する必要があります
@interface NSStringArray : NSArray
なので
- (NSString *)objectAtIndex:(NSUInteger)index
NSArrayにNSStringのみが含まれるようにします。
作成されたサブクラスは、ドロップイン置換として使用でき、コンパイラ警告、プロパティアクセス、より優れたコード作成、およびXcodeでの完了など、多くの便利な機能を提供します。これらはすべてコンパイル時の機能であり、実際の実装を再定義する必要はありません-NSArrayのメソッドは引き続き使用できます。
これを自動化して2つのステートメントだけに要約できるため、ジェネリックスをサポートする言語に近づけることができます。WMGenericCollectionでオートメーションを作成しましたテンプレートがCプリプロセッサマクロとして提供される。
マクロを含むヘッダーファイルをインポートした後、2つのステートメントを使用して汎用NSArrayを作成できます。1つはインターフェース用で、もう1つは実装用です。格納するデータ型とサブクラスの名前を指定するだけで済みます。WMGenericCollectionはNSArray
、NSDictionary
と、およびNSSet
それらに対応する変更可能なテンプレートにこのようなテンプレートを提供します。
例:次のステートメントで作成さList<int>
れると呼ばれるカスタムクラスによって実現できますNumberArray
。
WMGENERICARRAY_INTERFACE(NSNumber *, // type of the value class
// generated class names
NumberArray, MutableNumberArray)
を作成NumberArray
したら、プロジェクトのどこでも使用できます。の構文は<int>
ありませんが、独自の命名方式を選択して、これらをクラスとしてテンプレートとしてラベル付けできます。
を見てみましょう:
https://github.com/tomersh/Objective-C-Generics
プロトコルチェックメカニズムを転用することで、これは一種の貧乏人のジェネリックであるように見えます。
AppleおよびGNUStepフレームワークによって提供されるコレクションクラスは、それらがオブジェクトを与えられていると仮定するという点で半ジェネリックであり、ソート可能なものや特定のメッセージに応答するものがあります。float、intなどのプリミティブの場合、すべてのC配列構造はそのままで使用でき、一般的なコレクションクラス(NSNumberなど)で使用するための特別なラッパーオブジェクトがあります。さらに、Collectionクラスは、任意のタイプのオブジェクトを受け入れるようにサブクラス化(またはカテゴリを介して具体的に変更)できますが、すべてのタイプ処理コードを自分で記述する必要があります。メッセージは任意のオブジェクトに送信できますが、オブジェクトに不適切な場合はnullを返すか、メッセージを適切なオブジェクトに転送する必要があります。真のタイプのエラーは、実行時ではなくコンパイル時にキャッチする必要があります。実行時には、それらを処理または無視する必要があります。最後に、Objcは、トリッキーなケースを処理するための実行時リフレクション機能を提供し、メッセージが送信される前、または不適切なコレクションに入れられる前に、オブジェクトでサービスをチェックできます。異なるライブラリとフレームワークは、コード応答がないメッセージを送信したときのオブジェクトの動作について、RTFMとは異なる規則を採用していることに注意してください。おもちゃのプログラムとデバッグビルド以外のほとんどのプログラムは、本当に失敗してメモリやディスクに不正なデータを書き込もうとしたり、不正な操作を実行したり(たとえば、ゼロで除算したり、それをキャッチしたりできる)、アクセスしたりしない限り、クラッシュする必要はありません。システムリソースを制限します。Objective-Cのダイナミズムとランタイムは、物事が正常に失敗することを可能にし、コードに組み込む必要があります。(ヒント)関数の汎用性に問題がある場合、いくつかの特異性を試してください。特定の型で関数を上書きし、ランタイムで適切なメンバー関数をランタイムに選択させます(そのため、それらはセレクターと呼ばれます!)。
Example:
-(id) sort (id) obj; // too generic. catches all.
// better
-(id) sort: (EasilySortableCollection*) esc;
-(id) sort: (HardToSortCollection*) hsc;
...
[Sorter sort: MyEasyColl];
[Sorter sort: MyHardColl];