11月2010

NSPredicate の使い方

NSPredicate の話を求めて、このブログへ訪れる方が多いようなので、NSPredicate の使い方を紹介しようと思います。

NSPredicate は、NSArray で要素をフィルターするための条件を表したり、Core Data で取ってくるデータの条件を表すためのクラスです。

NSPredicate を作成する方法として最も簡単なものが、predicateWithFormat です。これを用いると、NSPredicate 独自の記法ではありますが、自然な記述で条件を書くことができます。

Predicate の条件式の基本形は、”keyPath operator literal” です。それぞれには、次のようなものが入ります。

keyPath
キー値コーディングのキーパス。この値が条件の判定に利用されます。self 指定すると、そのオブジェクト自体の値が利用されます。(selfもキー値コーディングです)
operator
比較演算子が入ります。数値であれば、=, < , >, < =, >= 等、文字列であれば =, contains, like, startsWith, endsWith, in 等が使用できます。
literal
定数値を入れます。キーパスとの比較に用いられます。数値は直接書き、文字列の場合は ‘ ‘ や ” ” で括ります。また、配列を { e0, e1, … } のような形式で記述できます。

また、メソッド名に Format があるように、NSLogと同様に書式指定子が使用できます。ただし、Predicate 用に追加されている書式指定子 %K があります。これは、keypath の部分を指定する書式指定子で、キーパスを入れるところには %@ ではなく、 %K を使うことに注意しなければなりません。

具体的な例を見れば、すぐにわかると思いますので、いくつかの例を示します。

NSArray *strings = [NSArray arrayWithObjects:@"first", @"second", @"third", nil];
NSPredicate *predicate;

// 単純な比較
predicate = [NSPredicate predicateWithFormat:@"self = 'first'"];
NSLog( @"%@", [strings filteredArrayUsingPredicate:predicate] );
// { first }

// 書式指定子を利用した比較
predicate = [NSPredicate predicateWithFormat:@"%K = %d", @"length", 5];
NSLog( @"%@", [strings filteredArrayUsingPredicate:predicate] );
// { first, third }

// in 演算子の利用と、配列のリテラル値の入力方法
predicate = [NSPredicate predicateWithFormat:@"self in %@", [NSArray arrayWithObjects:@"first", @"second", nil]];
NSLog( @"%@", [strings filteredArrayUsingPredicate:predicate] );
// { first, second }

// contains 演算子の利用
predicate = [NSPredicate predicateWithFormat:@"%K contains %@", @"self", @"i"];
NSLog( @"%@", [strings filteredArrayUsingPredicate:predicate] );
// { first, third }

// like演算子と1オプションの利用
predicate = [NSPredicate predicateWithFormat:@"%K like1 %@", @"self", @"FIRST"];
NSLog( @"%@", [strings filteredArrayUsingPredicate:predicate] );
// { first }

また、not, and, or 等の演算子を利用できます。

predicate = [NSPredicate predicateWithFormat:@"not ( 1 <= length and length <= 5 )"];
NSLog( @"%@", [strings filteredArrayUsingPredicate:predicate] );
// { second }

演算子やPredicateの作成方法は、他にもあります。詳しくは、Predicate Programming Guide を御覧ください。

参考資料
Predicate Programming Guide
NSPredicate Class Reference

NSURLConnection+Blocks

まだあまり相手にされていない感じの Blocks ですが、使うことができるシーンは結構あります。

NSURLConnection を使用すると、簡単にWEBからリソースを取得できますが、多少記述が多くなってしまうのと、デリゲートによって処理が散らばってしまうのが問題です。そこで、Blocks を使って、よりモダンな書き方ができるようにしてみます。

今回は NSURLConnection にカテゴリとして追加することにしました。使い方はこんな感じになります。

 NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.lifeaether.com/"]];
[NSURLConnection sendRequest:request completeBlock:^( NSData *receivedData ) {
    NSString *html = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
    NSLog( @"%@", html );
    1;
} errorBlock:^( NSError *error ) {
    NSLog( @"%@", error );
}];

completeBlock がリソースの取得に成功したときに呼び出されるブロックです。引数に受信したデータが入っています。errorBlock が失敗したとき呼び出されるブロックです。引数にエラー内容が入ります。

カテゴリの実装です。
NSURLConnection+Blocks.h

#import <Foundation/Foundation.h>

@interface NSURLConnection (BlocksAddition)

+ (void)sendRequest:(NSURLRequest *)request completeBlock:( void (^)( NSData *receivedData ) )complete errorBlock:( void (^)( NSError *errror ) )error;

@end

NSURLConnection+Blocks.m

#import "NSURLConnection+Blocks.h"

@interface NSURLConnectionBlocksAddtionDelegate : NSObject {
@private
    NSMutableData *receivedData;
    void (^completeBlock)( NSData *receivedData );
    void (^errorBlock)( NSError *error );
}

- (id)initWithCompleteBlock:( void (^)( NSData *receivedData ) )compelete errorBlock:( void (^)( NSError *error ) )error;

@end

@implementation NSURLConnectionBlocksAddtionDelegate

- (id)initWithCompleteBlock:( void (^)( NSData *receivedData ) )compelete errorBlock:( void (^)( NSError *error ) )error
{
    self = [super init];
    if ( self ) {
        receivedData = [[NSMutableData alloc] init];
        completeBlock = [compelete copy];
        errorBlock = [error copy];
    }
    return self;
}

- (void)dealloc {
    [errorBlock release];
    [completeBlock release];
    [receivedData release];
    [super dealloc];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    [receivedData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [receivedData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    completeBlock( receivedData );
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    errorBlock( error );
}

@end

@implementation NSURLConnection (BlocksAddition)

+ (void)sendRequest:(NSURLRequest *)request completeBlock:( void (^)( NSData *receivedData ) )complete errorBlock:( void (^)( NSError *errror ) )error
{
    id delegate = [[[NSURLConnectionBlocksAddtionDelegate alloc] initWithCompleteBlock:complete errorBlock:error] autorelease];
    [self connectionWithRequest:request delegate:delegate];
}

@end

特に説明する部分はないかと思われますが、疑問が出そうなところを少し解説します。

sendRequest: メソッド内で NSURLConnectionBlocksAddtionDelegate インスタンスに autorelease していて、開放されるんじゃないかと思われる人がいるかもしれませんが、NSURLConnection は例外的に delegate を retain するので、開放される心配はありません。

initWithCompleteBlock: メソッド内で、引数のブロックを copy していますが、これはブロックが通常はスタック領域に確保され、関数から抜けだすと消失してしまうからです。copy することによってそれを回避します。それによって、releaseする必要も生じます。

MacBook Air 13インチ の使用レポート

MacBookAir 13インチ 128GB モデル メモリを4GB カスタマイズ を買ってから半月くらい経ちました。外出時かつ他のパソコン環境がないところでしか使わないので、まだあまり利用していませんが、電車の中で寝ていないときには大活躍しています。

いいところ

  • 起動が速い
  • 軽い
  • 画面が広い
  • キーボードが打ちやすい
  • SDカードが挿せる
  • バッテリーが持つ
  • 十分なスペック

わるいところ

  • キーボードの感覚
  • まれに画面が乱れて再起動せざるを得なくなる
  • CDが取り込めない
  • 文字がちっちゃい

いいところ – 詳細

見た目で驚き、持って驚き、そして起動して驚きます。起動速度がとにかく速いです。ばらつきがありますが、15〜30秒で起動します。本当に速い。

13インチなので、画面が十分な広さです。これなら、プログラミングも満足に行えます。長いメソッド名も、これでバッチリです。

SDカードを挿せます。デジカメの画像を簡単に取り込めるので iPhoto と連携して活躍します。

バッテリーが持ちます。開発作業なら、おそらく公式の7時間は持つことでしょう。ちゃんと測っていませんが、それくらいは持ちそうな気がします。

十分なスペックがあります。たいていの作業は、ストレス無く行うことができます。たまにひっかかりを感じたり、待ち時間を要する作業もありますが、この軽さ、バッテリーの持ちを考えると、とても素晴らしいです。

わるいところ – 詳細

キーをストロークしたときの感覚があまり良くありません。好みに依るところが大きいので、購入前に一度試してみましょう。感覚が悪いからと言って、打ちにくいわけではないので、実用上問題はないのかもしれません。

稀に画面が乱れて何もできなくなる現象が発生します。しかし、これは最近のアップデートで直ったかもしれません。

CDが取り込めません。ネットワークドライブから iTunes に読み込もうと思ったのですが、うまくできませんでした。調べてみると、著作権保護のかかったCDはネットワークドライブからインポートできないようです。

文字がやや小さいです。まだ許容範囲ですが、小さいなと感じる場面がたまにあります。

まとめ

どこへ行っても作業したくなる人に大変おすすめできます。iBook G4 12 inch を持っていた経験からすると、ポータビリティはとても重要で、とにかく、いつでも持ち運ぶ気にならなければ、ノートブックを買う意味がありません。

しかし、ポータビリティを重視しすぎると、スペックが十分でなかったり、バッテリーの持ちが悪かったりしますが、その点、この Air は絶妙なバランスを保っていると言えるでしょう。

11インチについて

軽いのはいいのですが、ややスペックと画面の広さ、そしてバッテリーの持ちに不満を感じそうだったので、13インチを選びました。迷っている方は、店頭で比較してから買うといいと思います。