Objective-C 3.0の言語仕様を妄想する

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

広告