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する必要も生じます。

コメント投稿