私のクラスには次のような大量の繰り返しコードがあります:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
非同期要求の問題は、さまざまな要求が発生し、それらをすべて1つのエンティティとして扱うように割り当てられたデリゲートがあり、多くの分岐コードといコードが実行を定式化し始める場合です。
どのようなデータを取得していますか?これが含まれている場合は、それを実行し、そうでない場合は他の操作を実行します。ビューをIDでタグ付けできるように、これらの非同期要求にタグ付けできると便利です。
複数の非同期要求を処理するクラスを管理するのにどの戦略が最も効率的か興味がありました。
関連付けられているNSURLConnectionによってキー設定されたCFMutableDictionaryRefで応答を追跡します。すなわち:
connectionToInfoMapping =
CFDictionaryCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
NSMutableDictionaryの代わりにこれを使用するのは奇妙に思えるかもしれませんが、このCFDictionaryはキー(NSURLConnection)のみを保持しますが、NSDictionaryはキーをコピーします(そしてNSURLConnectionはコピーをサポートしません)。
完了したら:
CFDictionaryAddValue(
connectionToInfoMapping,
connection,
[NSMutableDictionary
dictionaryWithObject:[NSMutableData data]
forKey:@"receivedData"]);
そして今、接続に関する情報を追跡するために使用できる各接続のデータの「情報」ディクショナリがあり、「情報」ディクショナリには、返信データを受信時に格納するために使用できる可変データオブジェクトがすでに含まれています。
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSMutableDictionary *connectionInfo =
CFDictionaryGetValue(connectionToInfoMapping, connection);
[[connectionInfo objectForKey:@"receivedData"] appendData:data];
}
2つの異なるNSURLConnectionがあるプロジェクトがあり、同じデリゲートを使用したいと考えています。私がやったのは、クラスごとに2つのプロパティを作成することでした。それぞれの接続に1つです。次に、デリゲートメソッドで、どの接続であるかを確認します
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (connection == self.savingConnection) {
[self.savingReturnedData appendData:data];
}
else {
[self.sharingReturnedData appendData:data];
}
}
これにより、必要なときに特定の接続を名前でキャンセルすることもできます。
NSURLConnectionをサブクラス化してデータを保持することは、クリーンであり、他の回答のいくつかよりもコードが少なく、柔軟性が高く、参照管理についてあまり考える必要がありません。
// DataURLConnection.h
#import <Foundation/Foundation.h>
@interface DataURLConnection : NSURLConnection
@property(nonatomic, strong) NSMutableData *data;
@end
// DataURLConnection.m
#import "DataURLConnection.h"
@implementation DataURLConnection
@synthesize data;
@end
NSURLConnectionと同じように使用し、データプロパティにデータを蓄積します。
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
((DataURLConnection *)connection).data = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[((DataURLConnection *)connection).data appendData:data];
}
それでおしまい。
さらに先に進みたい場合は、さらに2、3行のコードでコールバックとして機能するブロックを追加できます。
// Add to DataURLConnection.h/.m
@property(nonatomic, copy) void (^onComplete)();
次のように設定します。
DataURLConnection *con = [[DataURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
con.onComplete = ^{
[self myMethod:con];
};
[con start];
そして、ロードが次のように終了したら呼び出します:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
((DataURLConnection *)connection).onComplete();
}
ブロックを拡張してパラメーターを受け入れるか、DataURLConnectionを引数として引数なしのブロック内で必要なメソッドに渡すことができます
これはIS新たな回答ではありません。
同じクラスのデリゲートメソッド内で異なるNSURLConnectionを区別するには、NSMutableDictionaryを使用して、(NSString *)description
をキーとしてNSURLConnectionを設定および削除します。
setObject:forKey
に選択したオブジェクトは、NSURLRequest
を使用するNSURLConnection
の開始に使用される一意のURLです。
NSURLConnectionが設定されると、
-(void)connectionDidFinishLoading:(NSURLConnection *)connection, it can be removed from the dictionary.
// This variable must be able to be referenced from - (void)connectionDidFinishLoading:(NSURLConnection *)connection
NSMutableDictionary *connDictGET = [[NSMutableDictionary alloc] init];
//...//
// You can use any object that can be referenced from - (void)connectionDidFinishLoading:(NSURLConnection *)connection
[connDictGET setObject:anyObjectThatCanBeReferencedFrom forKey:[aConnectionInstanceJustInitiated description]];
//...//
// At the delegate method, evaluate if the passed connection is the specific one which needs to be handled differently
if ([[connDictGET objectForKey:[connection description]] isEqual:anyObjectThatCanBeReferencedFrom]) {
// Do specific work for connection //
}
//...//
// When the connection is no longer needed, use (NSString *)description as key to remove object
[connDictGET removeObjectForKey:[connection description]];
私が取ったアプローチの1つは、各接続のデリゲートと同じオブジェクトを使用しないことです。代わりに、起動される接続ごとに解析クラスの新しいインスタンスを作成し、デリゲートをそのインスタンスに設定します。
これらすべてを処理するカスタムクラス MultipleDownload を試してください。
iOS5以降では、クラスメソッドsendAsynchronousRequest:queue:completionHandler:
応答は完了ハンドラーで返されるため、接続を追跡する必要はありません。
通常、辞書の配列を作成します。各辞書には、少しの識別情報、応答を保存するNSMutableDataオブジェクト、および接続自体があります。接続デリゲートメソッドが起動すると、接続の辞書を検索し、それに応じて処理します。
1つのオプションは、NSURLConnectionを自分でサブクラス化し、-tagまたは同様のメソッドを追加することです。 NSURLConnectionの設計は意図的に非常に単純なものなので、これは完全に受け入れられます。
または、接続のデータの作成と収集を担当するMyURLConnectionControllerクラスを作成できます。ロードが完了したら、メインコントローラーオブジェクトに通知するだけで済みます。
他の回答で指摘されているように、connectionInfoをどこかに保存し、接続ごとに検索する必要があります。
このための最も自然なデータ型はNSMutableDictionary
ですが、接続がコピーできないため、NSURLConnection
をキーとして受け入れることはできません。
NSURLConnections
のキーとしてNSMutableDictionary
を使用する別のオプションは、NSValue valueWithNonretainedObject]
:
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
NSValue *key = [NSValue valueWithNonretainedObject:aConnection]
/* store: */
[dict setObject:connInfo forKey:key];
/* lookup: */
[dict objectForKey:key];
ASIHTTPRequest が好きです。
NSURLConnectionをサブクラス化し、タグ、デリゲート、およびNSMutabaleDataを追加することにしました。要求を含むすべてのデータ管理を処理するDataControllerクラスがあります。 DataControllerDelegateプロトコルを作成しました。これにより、個々のビュー/オブジェクトがDataControllerをリッスンして、要求がいつ完了したか、必要な場合はどれだけダウンロードされたか、またはエラーを検出できます。 DataControllerクラスは、NSURLConnectionサブクラスを使用して新しいリクエストを開始し、DataControllerをリッスンするデリゲートを保存して、リクエストがいつ終了したかを知ることができます。これは、XCode 4.5.2およびiOS 6での私の作業ソリューションです。
DataControllerDelegateプロトコルを宣言するDataController.hファイル。 DataControllerもシングルトンです:
@interface DataController : NSObject
@property (strong, nonatomic)NSManagedObjectContext *context;
@property (strong, nonatomic)NSString *accessToken;
+(DataController *)sharedDataController;
-(void)generateAccessTokenWith:(NSString *)email password:(NSString *)password delegate:(id)delegate;
@end
@protocol DataControllerDelegate <NSObject>
-(void)dataFailedtoLoadWithMessage:(NSString *)message;
-(void)dataFinishedLoading;
@end
DataController.mファイルの主要なメソッド:
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection;
NSLog(@"DidReceiveResponse from %@", customConnection.tag);
[[customConnection receivedData] setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection;
NSLog(@"DidReceiveData from %@", customConnection.tag);
[customConnection.receivedData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection;
NSLog(@"connectionDidFinishLoading from %@", customConnection.tag);
NSLog(@"Data: %@", customConnection.receivedData);
[customConnection.dataDelegate dataFinishedLoading];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection;
NSLog(@"DidFailWithError with %@", customConnection.tag);
NSLog(@"Error: %@", [error localizedDescription]);
[customConnection.dataDelegate dataFailedtoLoadWithMessage:[error localizedDescription]];
}
リクエストを開始するには:[[NSURLConnectionWithDelegate alloc] initWithRequest:request delegate:self startImmediately:YES tag:@"Login" dataDelegate:delegate];
NSURLConnectionWithDelegate.h:@protocol DataControllerDelegate;
@interface NSURLConnectionWithDelegate : NSURLConnection
@property (strong, nonatomic) NSString *tag;
@property id <DataControllerDelegate> dataDelegate;
@property (strong, nonatomic) NSMutableData *receivedData;
-(id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString *)tag dataDelegate:(id)dataDelegate;
@end
NSURLConnectionWithDelegate.m:
#import "NSURLConnectionWithDelegate.h"
@implementation NSURLConnectionWithDelegate
-(id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString *)tag dataDelegate:(id)dataDelegate {
self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately];
if (self) {
self.tag = tag;
self.dataDelegate = dataDelegate;
self.receivedData = [[NSMutableData alloc] init];
}
return self;
}
@end
すべてのNSURLConnectionにはハッシュ属性があり、この属性によってすべてを区別できます。
たとえば、接続の前後に特定の情報を保持する必要があるため、RequestManagerにはこれを行うNSMutableDictionaryがあります。
例:
// Make Request
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *c = [[NSURLConnection alloc] initWithRequest:request delegate:self];
// Append Stuffs
NSMutableDictionary *myStuff = [[NSMutableDictionary alloc] init];
[myStuff setObject:@"obj" forKey:@"key"];
NSNumber *connectionKey = [NSNumber numberWithInt:c.hash];
[connectionDatas setObject:myStuff forKey:connectionKey];
[c start];
リクエスト後:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@"Received %d bytes of data",[responseData length]);
NSNumber *connectionKey = [NSNumber numberWithInt:connection.hash];
NSMutableDictionary *myStuff = [[connectionDatas objectForKey:connectionKey]mutableCopy];
[connectionDatas removeObjectForKey:connectionKey];
}