objective-c 管理多个异步 NSURLConnection 连接
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/332276/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Managing multiple asynchronous NSURLConnection connections
提问by Coocoo4Cocoa
I have a ton of repeating code in my class that looks like the following:
我的班级中有大量重复代码,如下所示:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
The problem with asynchronous requests is when you have various requests going off, and you have a delegate assigned to treat them all as one entity, a lot of branching and ugly code begins to formulate going:
异步请求的问题在于,当您有各种请求发出,并且您分配了一个委托来将它们全部视为一个实体时,许多分支和丑陋的代码开始形成:
What kind of data are we getting back? If it contains this, do that, else do other. It would be useful I think to be able to tag these asynchronous requests, kind of like you're able to tag views with IDs.
我们要取回什么样的数据?如果它包含这个,做那个,否则做其他。我认为能够标记这些异步请求会很有用,就像您能够使用 ID 标记视图一样。
I was curious what strategy is most efficient for managing a class that handles multiple asynchronous requests.
我很好奇什么策略对于管理处理多个异步请求的类最有效。
采纳答案by Matt Gallagher
I track responses in an CFMutableDictionaryRef keyed by the NSURLConnection associated with it. i.e.:
我在由与其关联的 NSURLConnection 键控的 CFMutableDictionaryRef 中跟踪响应。IE:
connectionToInfoMapping =
CFDictionaryCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
It may seem odd to use this instead of NSMutableDictionary but I do it because this CFDictionary only retains its keys (the NSURLConnection) whereas NSDictionary copies its keys (and NSURLConnection doesn't support copying).
使用它而不是 NSMutableDictionary 似乎很奇怪,但我这样做是因为这个 CFDictionary 只保留它的键(NSURLConnection)而 NSDictionary 复制它的键(并且 NSURLConnection 不支持复制)。
Once that's done:
一旦完成:
CFDictionaryAddValue(
connectionToInfoMapping,
connection,
[NSMutableDictionary
dictionaryWithObject:[NSMutableData data]
forKey:@"receivedData"]);
and now I have an "info" dictionary of data for each connection that I can use to track information about the connection and the "info" dictionary already contains a mutable data object that I can use to store the reply data as it comes in.
现在我有每个连接的“信息”数据字典,我可以用它来跟踪有关连接的信息,“信息”字典已经包含一个可变数据对象,我可以用它来存储收到的回复数据。
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSMutableDictionary *connectionInfo =
CFDictionaryGetValue(connectionToInfoMapping, connection);
[[connectionInfo objectForKey:@"receivedData"] appendData:data];
}
回答by jbarnhart
I have a project where I have two distinct NSURLConnections, and wanted to use the same delegate. What I did was create two properties in my class, one for each connection. Then in the delegate method, I check to see if which connection it is
我有一个项目,其中有两个不同的 NSURLConnections,并且想使用相同的委托。我所做的是在我的班级中创建两个属性,每个连接一个。然后在委托方法中,我检查它是否是哪个连接
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (connection == self.savingConnection) {
[self.savingReturnedData appendData:data];
}
else {
[self.sharingReturnedData appendData:data];
}
}
This also allows me to cancel a specific connection by name when needed.
这也允许我在需要时按名称取消特定连接。
回答by Pat Niemeyer
Subclassing NSURLConnection to hold the data is clean, less code than some of the other answers, is more flexible, and requires less thought about reference management.
子类化 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
Use it as you would NSURLConnection and accumulate the data in its data property:
像使用 NSURLConnection 一样使用它并在其 data 属性中累积数据:
- (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];
}
That's it.
就是这样。
If you want to go further you can add a block to serve as a callback with just a couple more lines of code:
如果你想更进一步,你可以添加一个块作为回调,只需多几行代码:
// Add to DataURLConnection.h/.m
@property(nonatomic, copy) void (^onComplete)();
Set it like this:
像这样设置:
DataURLConnection *con = [[DataURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
con.onComplete = ^{
[self myMethod:con];
};
[con start];
and invoke it when loading is finished like this:
并在加载完成时调用它,如下所示:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
((DataURLConnection *)connection).onComplete();
}
You can extend the block to accept parameters or just pass the DataURLConnection as an argument to the method that needs it within the no-args block as shown
您可以扩展块以接受参数或仅将 DataURLConnection 作为参数传递给无参数块中需要它的方法,如图所示
回答by petershine
THIS IS NOT A NEW ANSWER. PLEASE LET ME SHOW YOU HOW I DID
这不是一个新答案。请让我告诉你我是怎么做的
To distinguish different NSURLConnection within same class's delegate methods, I use NSMutableDictionary, to set and remove the NSURLConnection, using its (NSString *)descriptionas key.
为了在同一个类的委托方法中区分不同的 NSURLConnection,我使用 NSMutableDictionary 来设置和删除 NSURLConnection,使用它(NSString *)description作为键。
The object I chose for setObject:forKeyis the unique URL that is used for initiating NSURLRequest, the NSURLConnectionuses.
我选择的对象setObject:forKey是用于启动NSURLRequest、NSURLConnection使用的唯一 URL 。
Once set NSURLConnection is evaluated at
一旦设置 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]];
回答by Brad The App Guy
One approach I've taken is to not use the same object as the delegate for each connection. Instead, I create a new instance of my parsing class for each connection that is fired off and set the delegate to that instance.
我采取的一种方法是不使用与每个连接的委托相同的对象。相反,我为每个被触发的连接创建了一个解析类的新实例,并将委托设置为该实例。
回答by leonho
Try my custom class, MultipleDownload, which handles all these for you.
试试我的自定义类MultipleDownload,它为您处理所有这些。
回答by Mike Abdullah
One option is just to subclass NSURLConnection yourself and add a -tag or similar method. The design of NSURLConnection is intentionally very bare bones so this is perfectly acceptable.
一种选择是自己继承 NSURLConnection 并添加 -tag 或类似方法。NSURLConnection 的设计故意非常简单,所以这是完全可以接受的。
Or perhaps you could create a MyURLConnectionController class that is responsible for creating and collecting a connection's data. It would then only have to inform your main controller object once loading is finished.
或者,您可以创建一个 MyURLConnectionController 类,负责创建和收集连接的数据。一旦加载完成,它只需要通知您的主控制器对象。
回答by Ben Gottlieb
I usually create an array of dictionaries. Each dictionary has a bit of identifying information, an NSMutableData object to store the response, and the connection itself. When a connection delegate method fires, I look up the connection's dictionary and handle it accordingly.
我通常创建一个字典数组。每个字典都有一些标识信息、一个 NSMutableData 对象来存储响应,以及连接本身。当连接委托方法触发时,我查找连接的字典并相应地处理它。
回答by Yariv Nissim
in iOS5 and above you can just use the class method
sendAsynchronousRequest:queue:completionHandler:
在 iOS5 及以上,你可以只使用类方法
sendAsynchronousRequest:queue:completionHandler:
No need to keep track of connections since the response returns in the completion handler.
由于响应在完成处理程序中返回,因此无需跟踪连接。
回答by mfazekas
As pointed out by other answers, you should store connectionInfo somewhere and look up them by connection.
正如其他答案所指出的,您应该将 connectionInfo 存储在某处并通过连接查找它们。
The most natural datatype for this is NSMutableDictionary, but it cannot accept NSURLConnectionas keys as connections are non copyable.
最自然的数据类型是NSMutableDictionary,但它不能接受NSURLConnection作为键,因为连接是不可复制的。
Another option for using NSURLConnectionsas keys in NSMutableDictionaryis using NSValue valueWithNonretainedObject]:
NSURLConnections用作键的另一个选项NSMutableDictionary是使用NSValue valueWithNonretainedObject]:
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
NSValue *key = [NSValue valueWithNonretainedObject:aConnection]
/* store: */
[dict setObject:connInfo forKey:key];
/* lookup: */
[dict objectForKey:key];

