最近のObjecitive-CにもSwiftに併せて細かな変更や機能追加がされており、NSArrayやNSDictionaryなどコンテナが保持する型を指定するLightweight Generics
が追加されたことも記憶に新しいですが、その他にも__kindof
を型に指定するアノテーションが追加されていました。
2016年、Objective-Cのコードをまだまだ保守していく皆さんのために、今回はこの__kindofについて軽くまとめておきます。
はじめに結論
まず結論から書いておきます。今北産業です。
NSArray *array1; // 何が入ってるかわからん
NSArray<_kindof UIView *> *array2; // UIViewとそのサブクラス入ってる
NSArray<UIView *> *array3; // UIViewしか入ってないもんね
- add/setは何でもできるが「取り出すときにどうすべきか」で警告される。
-
__kindof
を使わずに<UIView *>
の場合はサブクラスでさえ入ってないという解釈のコードである - 戻り値にも
__kindof
が指定できるので、UITableViewCellやUIViewControllerなど継承して利用することが頻繁に行われるクラスには指定してインターフェースをより細かく作りこむことが出来る
Lightweight Genericsに__kindofを使うとどうなる?
そもそも__kindof
がないと何が問題なのかを明確にするため、まず__kindof
を使わずにArrayを作る
NSMutableArray<UIView *> *array = @[].mutableCopy;
それに対して要素を次のように2つ追加する。UIViewとUIImageView
[array addObject:[[UIView alloc] init]];
[array addObject:[[UIImageView alloc] init]];
これらをそれぞれ次のように取り出す
UIView *first = array[0]; // もちろんこれは何も問題ない
UIImageView *second = array[1]; // 警告される
ここでarray[1]をUIImageViewに代入しようとすると警告される。
以上をわざわざスクリーンキャプチャで見せると次のような感じ
Lightweight Generics
を使ってコンテナに何が入っているかを明確にしたいが、実際はサブクラスが含まれている場合に警告されるようになっている。当然、キャストすれば警告はなくなるが、__kindofはサブクラスが入っていることを許可するために使われる。
NSMutableArray<__kindof UIView *> *array = @[].mutableCopy;
[array addObject:[[UIView alloc] init]];
[array addObject:[[UIImageView alloc] init]];
UIView *first = array[0]; // もちろんこれは何も問題ない
UIImageView *second = array[1]; // 警告されなくなる!!
しかし、まだ、__kindofの魅力はこんなもんじゃない
戻り値に__kindofを使うとどうなる?
もちろんメソッドの戻り値に__kindofを使うことも出来る。具体的にはUITableViewで頻繁に使う次のメソッドの戻り値にはすでに __kindof
が使われている(Xcode7.1.1で確認)。
- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier
forIndexPath:(NSIndexPath *)indexPath;
(そう、つまりみなさんはすでに__kindofを使っていたのだ!!ΩΩΩ<な、なんだってー)
このインターフェースを見ると、再利用キューに保持されていた様々なUITableViewCellの派生クラスを取り出せるということが分かるし、実際にキャストする必要性がないということも分かりやすい(idを戻り値とするよりずっと分かりやすい)。
参考URL
https://developer.apple.com/videos/play/wwdc2015-401/
http://stackoverflow.com/questions/31399208/ios-kindof-nsarray