Still Life

残念IT系母ちゃん。旦那さん、娘1歳、猫の4人暮らし。

Webサービスと通信するクラス

iPhone開発をはじめて約2ヶ月。基本文法もよくわかっていないままいきなりゴリゴリ書き始めたので、メチャクチャだろうと思いますが(クライアントには内緒)、かなり楽しいです。

Twitter連携をするアプリを作っているので、いきなりOAuthでの認証がわからなくて半泣きでしたが、素晴らしいことにライブラリを作って公開している方も、それの使い方をわかりやすく解説してくださる方もネット上にいっぱいいらっしゃいます。

ありがたいことです。

OAuthのライブラリをいじって、最新の認証方法に対応させることで、かなり勉強になりました。
以前にも書きましたが、使ったのは、
oauthconsumer、導入の仕方は、Objective-CでTwitter APIを使う 色々 - すぎゃーんメモを参考にさせていただきました。

さて、OAuthのライブラリを参考に、(Twitter以外の)Webサービスに接続してデータをとってくるクラスを作ったのですが、これが起動ごとに挙動が違ったり、落ちまくったりして、とても悩まされました。

いまだどうすればいいのかよくわからないのですが、一応動いているので載せます。もしたまたまこれを見て、有用な情報を教えてあげようという奇特な方がいらっしゃったら、ぜひコメントをいただきたいです。。。


やりたいのは、あるURLにPOSTして、戻って来た値を、このクラスの呼び出し元で取得したいだけです。
OADataFetcherクラスの完コピのつもりだったのですが、

  • 呼び出し元で、インスタンス化時にautoreleaseをつけると落ちる
  • 呼び出し元を保持するdelegateを、まんまdelegateと表記していると、NSURLConnectionのdelegateに差し変わってしまう?のか、これまた落ちる
  • 呼び出し元を保持するdelegateを、assignのままにしておくと、落ちる

という問題があり、結局下記のような実装になりました。
OADataFetcherでは落ちないのに、なぜだ・・・。

かなり簡略化しているけど・・・まだきっとまずい書き方いっぱいありそう。

DataCorresponder.h

#import <Foundation/Foundation.h>

@interface DataCorresponder : NSObject {
@private 
	id  myDelegate;
     SEL didFinishSelector;
     SEL didFailSelector;

    NSURLResponse *response;
    NSURLConnection *connection;
    NSMutableData *responseData;
	
	
}

@property (retain) id myDelegate;

// Webサービス送信
- (void)fetchDataWithRequest:(NSString *)aUser
					   apass:(NSString *)aPass
					   param:(NSString *)param
					delegate:(id)aDelegate 
		   didFinishSelector:(SEL)aFinishSelector 
			 didFailSelector:(SEL)aFailSelector;
//通信中止
- (void)stop;
@end

DataCorresponder.m


#import "DataCorresponder.h"

@implementation DataCorresponder

@synthesize  myDelegate;

- (id)init {
	[super init];
	responseData = [[NSMutableData alloc] init];
	return self;
}

- (void)dealloc {
	[connection cancel];
	[connection release];
	[response release];
	[responseData release];
	[myDelegate release];
	[super dealloc];
}

- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)aResponse {
	
	[response release];
	response = [aResponse retain];
	[responseData setLength:0];
	
}

- (void)connection:(NSURLConnection *)aConnection didFailWithError:(NSError *)error {
	[self.myDelegate performSelector:didFailSelector  withObject:error];
	
}

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

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

	if (responseData) {
		NSString* retString = [[[NSString alloc] 
							 initWithBytes: [responseData mutableBytes] 
							 length:[responseData length] 
							 encoding:NSUTF8StringEncoding] autorelease];
		[self.myDelegate performSelector:didFinishSelector withObject:retString];
		
	} else {
		
		NSException* nullError = [NSException 
								  exceptionWithName:@"からっぽエラー"
								  reason:@"戻り値がNULLです。"
								  userInfo:nil];
		[self.myDelegate performSelector:didFailSelector  withObject:nullError];
	}
	
	[response release];
	[responseData release];
	
}

- (void)fetchDataWithRequest:(NSString *)aUser
					   apass:(NSString *)aPass
					   param:(NSString *)param
					delegate:(id)aDelegate 
		   didFinishSelector:(SEL)aFinishSelector 
			 didFailSelector:(SEL)aFailSelector {
	
    self.myDelegate = aDelegate;
    didFinishSelector = aFinishSelector;
    didFailSelector = aFailSelector;
	
	//送信データ作成
	NSMutableString *bodyData = [NSMutableString stringWithFormat:@"auser=%@&apass=%@", aUser, aPass];
	
	if (param != NULL) {
		[bodyData appendFormat:@"&%@",param];
	}
	
	NSString *urlstr = @"http://gau.com/service.php";
	NSURL *url = [NSURL URLWithString:urlstr];
	NSData *myRequestData = [bodyData dataUsingEncoding:NSUTF8StringEncoding];
	NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL: url] autorelease];
	
	[request setHTTPMethod: @"POST"];
	[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"content-type"];
	[request setHTTPBody: myRequestData];

	connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];

}
- (void)stop {
	if (connection != nil) {
		[connection cancel];
		connection = nil;
	}
}

@end

fetchDataWithRequestとstopは自分が追加したのですが、connection:とついているメソッドはNSURLConnectionが呼び出すメソッドです。(言い方に語弊がありそうで自信ないけど、そういう理解。ところで、メソッド名の左側にも戻り値?がくっついているメソッドって一体何なんでしょうか。TableViewでもこういう書き方しているのがあるし。selectorで呼び出されるときに多い気がします。)

fetchDataWithRequestを外部から呼び出すと、最終的にconnectionDidFinishLoadingが呼ばれます。データはresponseData変数に溜まっているので、呼び出し元に定義したメソッドをコールして、このデータを戻り値に渡してやれば、呼び出し元で参照できます。

assignかretainかを勉強するために参照したサイトスタックスリー開発資料室 NSURLConnectionがdelegateをretainすることで起きうる問題を参照に、stopメソッドも作ってみました。
が、使ってません。
呼び出した後、このメソッドを呼び出し、さらにreleaseすると、あたりまえですが結果が帰ってこないうちに終わっちゃいますw

呼び出し元はこんなかんじ。

- (void)Yobidashi {
    [super viewDidLoad];
	
	NSString* param = [NSString stringWithFormat:@"param1=%@&param2=%d",
					   @"ぱらめた1", 2];
	
	DataCorresponder* sdd = [[DataCorresponder alloc] init];
	[sdd fetchDataWithRequest:@"ほげほげ"
						apass:@"ふがふが"
						param:param
					 delegate:self
			didFinishSelector:@selector(didFinishFetchingData:)
			  didFailSelector:@selector(didFailWithError:)];
	
	//[sdd stop];
	//[sdd release];
}
//正常
- (void)didFinishFetchingData:(NSString*) ret {
	NSLog(@"やったー!成功!");
}
//失敗
- (void)didFailWithError:(NSError *)error {
	NSLog(@"ざんねん!失敗しました。error=%@",error);
}