Objective-Cには強く型付けされたコレクションはありますか?


140

Mac / iPhoneプログラミングとObjective-Cは初めてです。C#とJavaには、「ジェネリックス」と呼ばれるコレクションクラスがあり、そのメンバーは宣言された型のみにすることができます。たとえば、C#では

Dictionary<int, MyCustomObject>

MyCustomObjectタイプの整数と値のキーのみを含めることができます。Objective-Cにも同様のメカニズムはありますか?


私自身、ObjCについて学び始めたばかりです。おそらく、ObjC ++を使用して重い作業を行うことができますか?
Toybuilderは2009年

あなたはこの質問への回答に興味があるかもしれません:NSArray、NSMutableArrayなどでタイピングを強制する方法はありますか?。なぜObjective-C / Cocoaでは一般的ではないのか、議論が交わされています。
mouviciel 2009年

2
ObjC ++は実際には言語ではありません... Cを処理するのと同じようにC ++をインラインで処理するObjCの機能を参照するためのより多くの方法です。ただし、(必要な場合など) C ++で作成されたサードパーティライブラリを使用する場合)。
マルクW


@マークW-「これをしてはいけない」理由は何ですか?私はObjC ++を使用しましたが、うまく機能します。#import <map>と@property std :: map <int、NSString *> myDictを実行できます。私は完全なCocoa APIを使用でき、強く型付けされたコレクションを持っています。欠点は見当たらない。
ジョンヘンケル

回答:


211

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にインポートされます。

Objective-C APIの操作


:私はメソッドで返さジェネリックやタイプについて質問がありますので、私は明確なすべてのものに保つために、別のスレッドで私の質問をstackoverflow.com/questions/30828076/...
LVP

2
@rizzes。はい、導入されました。
Connor

ここでの注意点の1つは、Swiftが汎用ObjCクラスの型注釈を完全に無視しないことです。たとえばMyClass <Foo: id<Bar>>、制約を指定した場合、Swiftコードは、値が制約のタイプであると想定します。これにより、何かを操作できます。ただし、特殊化されたサブクラスのMyClass特殊化されたタイプは無視されます(実質的にジェネリックと同じように見えますMyClass)。github.com/bgerstle/LightweightGenericsExampleを
ブライアンガートル

これは10.10、10.9、およびそれ以前のオペレーティングシステムでコンパイルできますか?
p0lAris 2015年

それらをサポートするように展開ターゲットを設定する限り、それは
Connor

91

この答えは時代遅れですが、歴史的な価値のために残っています。Xcode 7以降、2015年6月8日のConnorの回答はより正確です。


いいえ、独自のカスタムコレクションクラスでC ++テンプレートを使用する場合を除いて、Objective-Cにはジェネリックはありません(強くお勧めしません)。

Objective-Cには機能として動的型付けがあります。つまり、すべてのオブジェクトがメッセージを受信できるため、ランタイムはオブジェクトの型を気にしません。組み込みのコレクションにオブジェクトを追加すると、それらはtypeであるかのように扱われidます。ただし、心配しないでください。通常のように、これらのオブジェクトにメッセージを送信してください。これは正常に機能します(もちろん、コレクション内の1つ以上のオブジェクトが、送信しているメッセージに応答しない場合を除きます)

ジェネリックは、強力な静的型付き言語であるため、JavaやC#などの言語で必要です。Objective-Cの動的タイピング機能とはまったく異なる球技。


88
「心配しないで、それらのオブジェクトにメッセージを送信するだけ」には同意しません。これらのメッセージに応答しない間違ったタイプのオブジェクトをコレクションに入れると、ランタイムエラーが発生します。他の言語でジェネリックスを使用すると、コンパイル時のチェックでこの問題を回避できます。
henning77 2012年

8
@ henning77はい、ただしObjective-Cはこれらの言語よりも動的な言語です。強力な型保証が必要な場合は、それらの言語を使用してください。
Raffi Khatchadourian、2012年

36
また、心配しないという哲学にも同意しません。たとえば、最初の項目をNSArrayから取り出してNSNumberにキャストしたが、その項目が実際にはNSStringだった場合、ねじ込まれています...
jjxtra

13
@RaffiKhatchadourian-iOSアプリを作成している場合はあまり選択できません。Javaで簡単に作成でき、ネイティブアプリを作成することのすべての利点を享受できるとしたら、私を信じてください。
ericsoco 2013年

11
これについて私が抱えている最大の不満は、動的言語とコンパイル時間のチェックではなく、単純な開発者とのコミュニケーションにあります。どこかに記載されていない限り、プロパティ宣言を見て、それが返すオブジェクトのタイプを知ることはできません。
devios1 2013年

11

いいえ。ただし、わかりやすくするために、保存するオブジェクトのタイプでコメントを付けることができます。最近、Java 1.4で何かを記述する必要があるときに、これが数回行われたことを確認しました)例:

NSMutableArray* /*<TypeA>*/ arrayName = ....

または

NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...

これは、他の誰かがあなたのコードを読んだ場合に備えて、それを文書化するための良い方法だと思います。とにかく、変数の名前は、どのオブジェクトに含まれるかを知るために、できるだけ明確でなければなりません。
htafoya

6

Objective-Cにはジェネリックはありません。

ドキュメントから

配列は、オブジェクトの順序付けられたコレクションです。Cocoaは、NSArray、NSMutableArray(NSArrayのサブクラス)、およびNSPointerArrayのいくつかの配列クラスを提供します。


回答のドキュメントへのリンクは無効です - 「申し訳ありませんが、そのページは見つかりません」
パン


5

これは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];

それらに注意してください*


4

ジェネリック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はNSArrayNSDictionaryと、およびNSSetそれらに対応する変更可能なテンプレートにこのようなテンプレートを提供します。

例:次のステートメントで作成さList<int>れると呼ばれるカスタムクラスによって実現できますNumberArray

WMGENERICARRAY_INTERFACE(NSNumber *, // type of the value class
                         // generated class names
                         NumberArray, MutableNumberArray)

を作成NumberArrayしたら、プロジェクトのどこでも使用できます。の構文は<int>ありませんが、独自の命名方式を選択して、これらをクラスとしてテンプレートとしてラベル付けできます。


同じことがCoreLibにも存在することに注意してください: github.com/core-code/CoreLib/blob/master/CoreLib/CoreLib.h#L105
user1259710


2

夢が実現しました-今日からObjective-Cにジェネリックがあります(ありがとう、WWDC)。それは冗談ではありません-Swiftの公式ページで:

新しい構文機能により、言語全体の一貫性を向上させながら、より表現力豊かなコードを記述できます。SDKは、ジェネリックやnull可能性アノテーションなどの新しいObjective-C機能を採用して、Swiftコードをよりクリーンで安全なものにしています。以下はSwift 2.0の機能強化のほんの一例です。

そしてこれを証明する画像:Objective-Cジェネリック


2

ここに飛び込みたいだけです。Genericsについてのブログ投稿をここに書きました。

私が貢献したいのは、Genericsは、Appleが示すようなコレクションクラスだけでなく、どのクラスにも追加できるということです。

その後、さまざまなクラスに追加することに成功しました。これらのクラスは、Appleのコレクションとまったく同じように機能するからです。すなわち。コンパイル時間のチェック、コード補完、キャストの削除などを可能にします。

楽しい。


-2

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];
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.