[iOS] iOS5以下 + Xcode4.4でもObjective-Cの新シンタックスで配列、辞書の値を取得したい

2012/07/27

こんにちは。きんくまです。またタイトル長いです。

ちょっと前に、Objective-Cでも新シンタックスで楽にかけるようになるらしいという記事を書きました。
>> [iOS] 秋ぐらいに出る?らしいXcode4.5でコードを少し短くかけるみたい

そしたら、機能のMountain Lionさんと一緒にリリースされたXcode4.4でもできるらしいじゃないですか。
>> 今日からライオンでも使える!Xcode4.4 Modern Objective-C Syntaxでコードをきれいにする方法 | Zero4Racer PRO Developer’s Blog

ほうほうこれは便利そうだということで、早速ダウンロードして試してみたところ、

・NSNumberの@を使った書き方
・空カテゴリでメソッドを定義しなくてもOK
・@synthesizeをかかなくてもよい

なところはできました。

配列と辞書の値の取得ができない

それで配列と、辞書のところで詰まりました。
値の設定はできるのですが、値の取得ができないのです。

    NSDictionary *dic = @{
         @"name":@"Mario"
        ,@"age":@"33"
        ,@"method":^{
            NSLog(@"Hello world");
        }
    };
    NSLog(@"name: %@, age: %@", dic[@"name"], dic[@"age"]); //ここでエラー
Expected method to read dictionary element not found on object of type *NSDictionary

上記のエラーが出ます。困ったなあと思い、ググってみると解決方法が見つかりました。
良かった良かった。

>> How to enable the new Objective-C object literals on iOS?
>> Using Subscripting With Xcode 4.4 and iOS 4.3+ (Peter Steinberger)

注意点

まず注意点をまとめると、

・iOS6からはこれからする解決方法をしなくてもよい。iOS5以下の場合に行う。
・LLVM4.0コンパイラーが必要(これがXcode4.4に入ってる)
・短縮系の書き方をするとARCLiteが書き換えてくれる
・短縮系はコンパイル時に変換されるもので、実行時(ランタイム)にされるものではない

ARCLiteというのはARC使ったときに使われるファイルらしいです。
ARCLite.mというのがあるみたいですが詳しいことはわかりませんです。
なので、ARCを使ってない人は
Linking > Other Linker Flags に -fobjc-arc をつけてね。

って書いてあったけど、私のはつけてなくても動いてます。よくわかんない。

解決方法

NSObject+subscripts.hというカテゴリを用意してこんなコードを書きます。
実装(.m)ファイルは必要ないです。ARCLiteが勝手にやってくれるらしい。

#import <Foundation/Foundation.h>

#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000
@interface NSObject (PSPDFSubscriptingSupport)

- (id)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
- (id)objectForKeyedSubscript:(id)key;

@end
#endif

__IPHONE_OS_VERSION_MAX_ALLOWED < 60000 という部分がiOSのバージョンチェックですね。 iOS5以下の場合のみのターゲットにしてます。 上のはNSObjectに全て書いてしまってますが、きちんと書きたいんだ!という場合はこんなふうに書くみたいです

#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000
@interface NSDictionary(subscripts)
– (id)objectForKeyedSubscript:(id)key;
@end

@interface NSMutableDictionary(subscripts)
– (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
@end

@interface NSArray(subscripts)
– (id)objectAtIndexedSubscript:(NSUInteger)idx;
@end

@interface NSMutableArray(subscripts)
– (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
@end
#endif

このカテゴリヘッダーを短縮系を使いたいところにimportすればOKです。
もしくは、全体に適用させたい場合は .pchファイルにこんな感じに追加しておきます。

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import "NSObject+subscripts.h" //追加
#endif

実際に使ってみる

blocksを使うときは、一度変数に代入したらうまくいった。

    NSDictionary *dic = @{
         @"name":@"Mario"
        ,@"age":@"33"
        ,@"method":^{
            NSLog(@"Hello world");
        }
    };
    NSLog(@"name: %@, age: %@", dic[@"name"], dic[@"age"]);
    void (^hello)(void) = dic[@"method"];
    hello();
    
    
    NSArray *arr = @[
         @"Peach"
        , @"27"
        , ^(int a, int b){
            return a + b;
    }];
    
    NSLog(@"name: %@, age: %@", arr[0], arr[1]);
    int (^add)(int,int) = arr[2];
    NSLog(@"5 + 3 = %d",add(5,3));

念のためもう一度書いておくと、今回の処理はiOS6以上では必要ないということです。
移行のための仮処置という。

あと、もっと詳しい機能について、例えば@synthesize時のインスタンス変数のこととかは
下記のページで勉強できます。

@shu223さんのツイートから


自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る