88

我的课堂上有大量重复代码,如下所示:

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

异步请求的问题是,当您有各种请求发出时,并且您分配了一个委托将它们全部视为一个实体,许多分支和丑陋的代码开始制定:

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

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

4

13 回答 13

77

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

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];
}
于 2008-12-01T22:37:37.457 回答
19

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


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

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

于 2011-01-24T20:18:22.950 回答
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 一样使用它,并在其 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];
}

就是这样。

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

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

于 2012-12-17T19:25:07.627 回答
8

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

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

我选择的对象setObject:forKey是用于启动NSURLRequestNSURLConnection用途的唯一 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]];
于 2010-07-06T22:44:52.693 回答
5

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

于 2008-12-02T05:35:18.567 回答
4

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

于 2008-12-02T08:51:47.467 回答
2

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

于 2008-12-01T21:38:23.847 回答
2

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

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

于 2008-12-02T00:20:52.850 回答
2

在iOS5及以上你可以只使用类方法 sendAsynchronousRequest:queue:completionHandler:

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

于 2012-12-13T00:06:56.783 回答
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];
于 2009-02-07T15:15:13.300 回答
1

我喜欢ASIHTTPRequest

于 2010-05-26T11:24:46.930 回答
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
于 2012-11-14T00:44:54.880 回答
0

每个 NSURLConnection 都有一个 hash 属性,你可以通过这个属性来区分。

例如,我需要在连接之前和之后维护某些信息,所以我的 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];
}
于 2013-12-23T10:27:47.487 回答