Modern Objective-Cで実現するシンプルコーディングのススメ
最近話題のシンプル&リーダブルなObjective-C “Modern Objective-C”について
一度ちゃんと調べたいと思ってたので記事にしてみました。
本エントリでは、Modern Objective-Cになると何がどう嬉しいのか、
古い書き方”Legacy”と新しい書き方”Modern”の比較を通して見ていきます。
ちなみに、Modern Objective-Cの詳細については、iOS Dev Centerにて公開されている
WWDC 2012の下記のセッションの資料にて詳しく説明されています。
また、日本語情報としては下記のエントリが参考になります。
ヘッダ
Legacy
#import <Foundation/NSObject.h> #import <Foundation/NSString.h>
Modern
#import <Foundation/Foundation.h>
集約したヘッダファイルができたので、
import文が一つで済むようになりました。
列挙型(Enum)
Legacy
(Mac OS X 10.5以前)
typedef enum {
CLUBS,
DIAMONDS,
HEARTS,
SPADES
} cardsuit;
(Mac OS X 10.5・iOS)
enum {
CLUBS,
DIAMONDS,
HEARTS,
SPADES
};
typedef NSUInteger cardsuit;
Modern
typedef enum cardsuit : NSUInteger {
CLUBS,
DIAMONDS,
HEARTS,
SPADES
} cardsuit;
Modernスタイルを使うと、
コード補完が改善し、型検査がより強化されるようです。
コードの保守性を考えると、Modernスタイルで記述するよう心がけたいですね。
プロパティ
Legacy
@interface Player : NSObject
@property(strong) NSString *name;
@end
@implementation Player {
NSString *_name;
}
@synthesize name = _name;
@end
Modern
@interface Player : NSObject @property(strong) NSString *name; @end @implementation Player @end
これにより、独自クラスのインスタンス変数の定義が
非常に短く記述できるようになりました。
もうプロパティに対応するインスタンス変数を
わざわざヘッダファイルで宣言する必要はありません。
ちなみに、”name”変数にアクセスするには、
変数名の前にアンダースコアをつけて”_name”とします。
これによってインスタンス変数とプロパティ名が混ざらないようにしているわけですね。
プロパティに対応するインスタンス変数については下記のエントリが参考になります。
リテラル
Legacy
// 整数
NSNumber *twentyFourInt = [NSNumber numberWithInt:24];
NSNumber *twentyFourUnsignedInt = [NSNumber numberWithUnsignedInt:24U];
NSNumber *twentyFourLong = [NSNumber numberWithLong:24L];
NSNumber *twentyFourLongLong = [NSNumber numberWithLongLong:24LL];
// 浮動小数点
NSNumber *piFloat = [NSNumber numberWithFloat:3.141592654f];
NSNumber *piDouble = [NSNumber numberWithDouble:3.1415926535];
// 真偽値
NSNumber *numYes = [NSNumber numberWithBool:YES];
NSNumber *numNo = [NSNumber numberWithBool:NO];
// 値のコピー
NSNumber *numCopy = [NSNumber numberWithFloat:x];
// パス文字列
NSString *path = [NSString stringWithUTF8String: getenv("PATH")];
Modern
// 整数
NSNumber *twentyFourInt = @24;
NSNumber *twentyFourUnsignedInt = @24U;
NSNumber *twentyFourLong = @24L;
NSNumber *twentyFourLongLong = @24LL;
// 浮動小数点
NSNumber *piFloat = @3.141592654f;
NSNumber *piDouble = @3.1415926535;
// 真偽値
NSNumber *numYes = @YES;
NSNumber *numNo = @NO;
// 値のコピー
NSNumber *numCopy = @(x);
// パス文字列
NSString *path = @( getenv("PATH") );
Modern Objective-Cのリテラルについては下記のドキュメントが参考になります。
コレクション
Legacy
// 定義
NSArray *array = [NSArray arrayWithObjects:@"array1", @"array2", @"array3", nil];
NSArray *arrayEmpty = [NSArray array];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
value1, @"key1",
value2, @"key2",
nil];
NSDictionary *dictionaryEmpty = [NSDictionary dictionary];
// 参照
id array1str = [array objectAtIndex:1];
id dictionaryKey1 = [dictionary objectForKey:@"key1"];
// 更新
[array replaceObjectAtIndex:1 withObject:@"array1New"];
[dictionary setObject:value1New forKey:@"key1"];
// ループ
int i;
for (i = 0; i < [array count]; i++) {
id element = [array objectAtIndex:i];
// 中身
}
Modern
// 定義
NSArray *array = @[ @"array1", @"array2", @"array3"];
NSArray *arrayEmpty = @[];
NSDictionary *dictionary = @{ @"key1" : value1, @"key2" : value2};
NSDictionary *dictionaryEmpty = @{};
// 参照
id array1str = array[1];
id dictionaryKey1 = dictionary[@"key1"];
// 更新
array[1] = @"array1New";
dictionary[@"key1"] = value1New;
// ループ
for (id element in array) {
// 中身
}
定義時には、NSArrayとNSDictionaryの最終要素をnilとする必要がなくなりました。
また、NSDictionaryについては明示的にKey-Valueの関係を記述できるようになりました。
参照、更新、ループでも非常にシンプルな記述が可能になっています。
なお、参照/更新では下記のエラーが発生します。
Expected method to read array element not found on object of type 'NSArray *'
iOS5以前の場合は、下記エントリに記載の方法で対処できます。
総評
全体を通して、古いObjective-Cならではの癖が緩和されて、
誰でも読みやすく、書きやすい、とても良質な改良が施されていると感じました。
ただ、コレクションのところで説明したように、
iOS5では一部魔改造が必要になってしまうところが残念ではあります。
iOS6では何の問題もなく使えるはずですので、一時的に我慢しましょう。
新規作成するコードはもちろん、既存のコードについてもModern Objective-C化を進めて
ソースコードをどんどんシンプルにしていきたいところです。