Objective-C
Xcode
iOS

Xcode7時代のObjective-C

More than 3 years have passed since last update.

Swift2の発表で活気付く中、Objective-Cもひっそり進化しています。

iOSアプリは何個か作ってきたけど、急にSwiftに移行するのは……という方!

Xcode7で使えるようになったモダンな文法をちょっとだけ採用してみませんか?

きっと手元のコードが美しく、保守しやすくなりますよ!


新しく使えるようになった文法

新しく使えるようになった……と、いっても2つだけです(´・ω・`)


  • Generics

  • Nullability Annotation


Generics

(1)  ArrayやDictionaryに追加するオブジェクトの制限

特定クラスのインスタンスだけをArrayやDictionaryに追加したいケースで有益です。

今までのObjective-Cでは、こんなコードを書いてインスタンスの型チェックを行う必要がありました。

if ([[hoge class] isSubclassOfClass:[NSObject class]]) {

// NSObjectのサブクラスだったらArrayに追加
[array addObject:hoge];
}

Genericsを使うとArrayやDictionaryの宣言時に格納する型を指定できます。

// Arrayに格納できるのはNSStringだけ!

NSMutableArray<NSString*> *array = [NSMutableArray array];

// NSStringなので格納できる
[array addObject:@"波動拳"];

// NSStringではないのでコンパイル時にエラー
[array addObject:[[NSObject alloc]init]];

Dictionaryでも同じことができます。

NSMutableDictionary<NSString*, NSObject*> *dic = [NSMutableDictionary dictionary];

[dic setObject:[[NSObject alloc] init] forKey:@"hoge"];

※Dictionaryの場合、Keyに指定する部分を”KeyType:クラス型”にするらしいが、なぜかコンパイルエラーになる……。

Using Swift with Cocoa and Objective-C

(2) Genericsを用いたクラス設計

もちろん、ArrayやDictionaryだけでなくクラス作成時にもGenericsは使えます。

変数やメソッド引数の型を流動的に変更したい……なんてときに便利ですね。

// Tに相当するクラス型をインスタンス生成時に指定します

@interface MyLogic<T> : NSObject

@end

外部から与えるクラス型に制限を付けることもできます。

// NSStringクラスのサブクラスだけ許容します

@interface MyLogic<T:NSString *> : NSObject

// 引数の型がインスタンス生成時に決まるメソッド
- (void)myMethod:(T)arg;

@end

このあたりの仕組みを上手くつかってやると

// NSStringクラスのサブクラスだけ許容します

MyLogic<NSString*> *hoge = [[MyLogic alloc] init];
// メソッドの型はNSString
[hoge myMethod:@"昇竜拳"];

みたいなことができます。


Nullability Annotation

難しく書いていますが、SwiftのOptionalとほぼ同じでしょうか?

変数やメソッド定義時にnilが代入されうるかどうか定義できるようになりました。

// nilを許容するプロパティ

@property(nonatomic, strong) __nullable NSString *nilOK;
// nilを許容しないプロパティ
@property(nonatomic, strong) __nonnull NSString *nilNG;

ちなみに以下のコードでも同じ意味になります。

// nilを許容するプロパティ

@property(nonatomic, strong, nullable) NSString *nilOK;
// nilを許容しないプロパティ
@property(nonatomic, strong, nonnull) NSString *nilNG;

また、nonnull限定ですが以下のようにマクロを使った範囲指定も可能です。

NS_ASSUME_NONNULL_BEGIN

@property(nonatomic, strong) NSString *nilNG1;
@property(nonatomic, strong) NSString *nilNG2;
NS_ASSUME_NONNULL_END

それぞれ値を入れてみます。

self.nilOK = nil;

// こちらだけコンパイル警告が表示されます
self.nilNG = nil;

__nonnull属性を付けると、nil代入時に警告してくれます。

メソッド定義でも同じですね。

// nilを受け取らず、nilを返却しないメソッド

- (__nonnull NSString*)nonnullMethod:(__nonnull NSString*)string;

これもnilを指定して実行すると警告してくれます。

この仕組みを使うと、変数やメソッドの仕様(nilを許容するかどうか)を開発者に分かりやすく伝えることができます。

「このメソッドの戻り値は、nilを返す可能性あるからチェックが必要だね!」

「このメソッドは、nilを受け付けないから実行前に確認を……」

なんてのが阿吽の呼吸で伝わると素晴らしいと思います。

有効活用するとみんな幸せになれそうです。