管理多个异步NSURLConnection连接

时间:2008-12-01 21:21:33

标签: objective-c iphone cocoa cocoa-touch nsurlconnection

我班上有很多重复的代码,如下所示:

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

异步请求的问题在于,当您有各种请求消失,并且您指定了一个委托将它们全部视为一个实体时,许多分支和丑陋的代码开始形成:

我们回来了什么样的数据?如果它包含这个,那就做,否则做其他。我认为能够标记这些异步请求会很有用,就像你能够使用ID标记视图一样。

我很好奇什么策略对于管理处理多个异步请求的类最有效。

13 个答案:

答案 0 :(得分:77)

我跟踪由与之关联的NSURLConnection键入的CFMutableDictionaryRef中的响应。即:

connectionToInfoMapping =
    CFDictionaryCreateMutable(
        kCFAllocatorDefault,
        0,
        &kCFTypeDictionaryKeyCallBacks,
        &kCFTypeDictionaryValueCallBacks);

使用它代替NSMutableDictionary似乎很奇怪,但我这样做是因为这个CFDictionary只保留其键(NSURLConnection),而NSDictionary复制其键(NSURLConnection不支持复制)。

完成后:

CFDictionaryAddValue(
    connectionToInfoMapping,
    connection,
    [NSMutableDictionary
        dictionaryWithObject:[NSMutableData data]
        forKey:@"receivedData"]);

现在我有一个“info”数据字典用于每个连接,我可以用它来跟踪有关连接的信息,“info”字典已经包含一个可变数据对象,我可以使用它来存储回复数据,因为它进来了。

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSMutableDictionary *connectionInfo =
        CFDictionaryGetValue(connectionToInfoMapping, connection);
    [[connectionInfo objectForKey:@"receivedData"] appendData:data];
}

答案 1 :(得分:19)

我有一个项目,我有两个不同的NSURLConnections,并希望使用相同的委托。我所做的是在我的类中创建两个属性,每个连接一个。然后在委托方法中,我检查它是否是

的连接

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    if (connection == self.savingConnection) {
        [self.savingReturnedData appendData:data];
    }
    else {
        [self.sharingReturnedData appendData:data];
    }
}

这也允许我在需要时按名称取消特定连接。

答案 2 :(得分:16)

对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];
}

就是这样。

如果你想更进一步,可以添加一个块作为回调,只需几行代码:

// 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作为参数传递给no-args块中需要它的方法,如图所示

答案 3 :(得分:8)

这不是一个新的答案。请让我看看我怎么做

为了区分同一个类的委托方法中的不同NSURLConnection,我使用NSMutableDictionary来设置和删除NSURLConnection,使用其(NSString *)description作为键。

我为setObject:forKey选择的对象是用于发起NSURLRequest的唯一网址,NSURLConnection使用。

设置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]];

答案 4 :(得分:5)

我采取的一种方法是不为每个连接使用与委托相同的对象。相反,我为每个被触发的连接创建了一个新的解析类实例,并将委托设置为该实例。

答案 5 :(得分:4)

尝试我的自定义类MultipleDownload,它会为您处理所有这些。

答案 6 :(得分:2)

我通常会创建一个字典数组。每个字典都有一些标识信息,一个用于存储响应的NSMutableData对象以及连接本身。当连接委托方法触发时,我查找连接的字典并相应地处理它。

答案 7 :(得分:2)

一个选项就是自己继承NSURLConnection并添加-tag或类似的方法。 NSURLConnection的设计是故意非常简单的骨骼,所以这是完全可以接受的。

或许您可以创建一个负责创建和收集连接数据的MyURLConnectionController类。然后,只需在加载完成后通知您的主控制器对象。

答案 8 :(得分:2)

在iOS5及以上版本中,您只需使用class方法即可 sendAsynchronousRequest:queue:completionHandler:

由于响应在完成处理程序中返回,因此无需跟踪连接。

答案 9 :(得分:1)

正如其他答案所指出的那样,你应该将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];

答案 10 :(得分:1)

我喜欢ASIHTTPRequest

答案 11 :(得分:0)

我决定继承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

答案 12 :(得分:0)

每个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];
}