Swift 3.0リリースのタイミングでObjective-C 3.0も一緒に出てくるんじゃないかな。希望は薄いですが、少なくともSwiftを意識した言語拡張は今後も続いてゆくでしょう。
Swift 3.0からCocoaフレームワークの命名規則が大幅に見直される予定ですので、@alias辺りはObjective-C側の互換性確保の対応としてあり得るかもしれません。
Nullability
// Before
- (nonnull NSArray *)split:(nullable NSString *)sep;
// After
- (! NSArray *)split:(? NSString *)sep;
- (NSArray *!)split:(NSString *?)sep;
- (NSArray!)split:(NSString?)sep;
型推論
id型は型推論の可能性を秘めています。id型の型推論を取り入れない手はないでしょう。
// Objective-C 3.0
id str = "a,b,c", sep = ",";
id ary = [str componentsSeparatedByString:sep];
if (ary.count) NSLog(ary.firstObject);
// Objective-C 2.0
id str = @"a,b,c", sep = @",";
id ary = [str componentsSeparatedByString:sep];
if ([ary count]) NSLog([ary firstObject]);
オートボクシング
プリミティブ型からオブジェクト型への暗黙的な変換をサポート。
// Before
NSString *str = @"string";
// After
NSString *str = "string";
// Before
[@"a,b" split:@","];
// After
["a,b" split:","];
// Before
NSArray *ary = @{@"foo", @"bar"};
NSDictionary *dic = @{@"key": @"Shop", @"val": @99};
// After
NSArray *ary = {"foo", "bar"};
NSDictionary *dic = {"key": "Shop", "val": 99};
NSDictionary *dic = (key:@"Shop" val:@99); // タプル to 辞書
タプル
(NSString *key, NSNumber *val) tuple = (key:@"Shop" val:@99);
[NSDictionary dictionaryWithTuple:tuple][@"key"]; // "Shop"
(id k, id v) = tuple;
tuple.key == k; // true
tuple.val == (_:k _:v).1; // true
id tpl = (insertObject:@"foo" atIndex:0);
[@[@"bar"] performTuple:tpl]; // ["foo", "bar"]
プロパティ/メッセージ・エイリアス
@interface NSString (MaryCore)
@alias len = length;
@alias split: = componentsSeparatedByString:;
@alias trimming: = stringByTrimmingCharactersInSet:;
@end
@interface NSMutableString (MaryCore)
@alias @selector(append:) = @selector(appendString:);
@end
展開イメージ
/* Test.m */
void test() {
[@"a,b" split:@","];
[@"a,b" componentsSeparatedByString:@","];
}
/* Test.x */
void _test() {
objc_msgSend(@"a,b", @selector(componentsSeparatedByString:), @",");
objc_msgSend(@"a,b", @selector(componentsSeparatedByString:), @",");
}
インライン・メッセージ
メッセージ式のインライン展開を強制する。メッセージの呼び出しコストが抑えられる。
展開イメージ
/* Yoda.m */
~ (void)aaa { printf("aaa"); } // インライン化必須
- (void)bbb { printf("bbb"); } // インライン化容認
- (void)ccc { printf("ccc"); } // インライン化容認
- (void)force {
[self aaa];
[self bbb];
[self ccc];
}
/* Yoda.x */
void `-[Yoda bbb]`(id self, SEL _cmd) { printf("bbb"); }
void `-[Yoda ccc]`(id self, SEL _cmd) { printf("ccc"); }
void `-[Yoda force]`(id self, SEL _cmd) {
printf("aaa");
printf("bbb");
objc_msgSend(self, @selector(ccc:));
}
インライン化を行うため、実行時にメソッドの実体は存在しない事になる。そのため[self performSelector:@selector(aaa)]
による動的な呼び出しは行えない。
メンバー関数
オーバーロード、ラベル引数、デフォルト引数に対応可能。
@implementation Yoda
- void force(int state, display:BOOL d = YES) {
printf("%p: %d, %d", self, state, d);
}
@end
void test() {
Yoda.new.force(state:1 display:NO);
Yoda.new.force(2);
}
展開イメージ
void Yoda_force_display_void_int_BOOL(id self, int state, BOOL display) {
printf("%p: %d, %d", self, state, display);
}
void _test() {
Yoda_force_display_void_int_BOOL(Yoda.alloc.init, 1, NO);
Yoda_force_display_void_int_BOOL(Yoda.alloc.init, 2, YES);
}
コンストラクタ
JavaやC++と同等の記法が可能に。
+ void init(NSString *name = @"Default") {
return [self.alloc initWithName:name];
}
void test() {
[Yoda() description];
[Yoda("Luke") description];
}
void _test() {
[[Yoda.alloc initWithName:@"Default"] description];
[[Yoda.alloc initWithName:@"Luke"] description];
}
余談
コンストラクタとメンバ関数の仕組みがあればもはやSwift/Java/C++と変わらない言語となる。
Yoda("Luke").force(9);
プロパティの遅延初期化・デフォルト初期化
これらにより、コンストラクタ内でのインスタンス初期化作業が不要に。
lazy
属性、default
属性にはオブジェクトのクラスメソッドを指定することも可能。
遅延初期化
@property (nonatomic, lazy=dictionary) NSMutableDictionary *map;
@property (nonatomic, lazy=blueColor) NSColor *color;
@property (nonatomic, lazy) NSMutableDictionary *map = [NSMutableDictionary dictionary];
@property (nonatomic, lazy) NSColor *color = [NSColor blueColor];
デフォルト初期化
@property (nonatomic, default=dictionary) NSMutableDictionary *map;
@property (nonatomic, default=blueColor) NSColor *color;
@property (nonatomic, default) NSMutableDictionary *map = [NSMutableDictionary dictionary];
@property (nonatomic, default) NSColor *color = [NSColor blueColor];
まとめ
@implementation NSString (MaryCore)
@alias split: = componentsSeparatedByString:;
~ NSArray! split(NSString? separator = @"") {
return [self split:separator];
}
~ NSString *trim() {
return [self stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];
}
+ (void)load {
@"a,b,c".split(","); // ["a", "b", "c"]
@"a,b,c".split(); // ["a,b,c"]
@"a,b,c".split(nil); // error: nil is not compatible with expected argument type 'NSString'
@" a ".trim(); // "a"
" a ".trim(); // error: Member reference base type 'const char *' is not a structure or union
}
@end