2008-12-01 110 views
88

我有一吨的重复代码在我的课,看起来像如下:管理多个异步NSURLConnection的连接

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

与异步请求的问题是当你有不同的要求去了,和你有一个委托分配给他们所有作为一个实体,很多分支和丑陋的代码开始制定去:

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

我很好奇哪种策略对于管理处理多个异步请求的类来说效率最高。

回答

77

我跟踪由与其关联的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]; 
} 
+0

由于可能有两个或多个异步连接一次可能进入委托方法,因此有什么具体的方法可以确保正确的行为? – PlagueHammer 2009-07-28 06:48:40

+0

(我在这里创建了一个新问题,询问此问题:http://stackoverflow.com/questions/1192294/cocoa-any-checks-required-for-multiple-asynchronous-nsurlconnections) – PlagueHammer 2009-07-28 07:02:28

+3

如果代理是从多个线程中调用。您必须使用互斥锁来保护数据结构。更好的解决方案是继承NSURLConnection并将响应和数据引用添加为实例变量。我在Nocturne的问题上提供了一个更详细的解释:http://stackoverflow.com/questions/1192294/cocoa-any-checks-required-for-multiple-asynchronous-nsurlconnections – 2009-12-21 05:11:24

2

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

+0

本,它会好起来的问你换了一块示例代码?我试图想象你是如何做到的,但并不是全部。 – Coocoo4Cocoa 2008-12-01 21:41:22

+0

尤其是本,你怎么查字典?由于NSURLConnection没有实现NSCopying(因此它不能用作关键字),因此不能使用字典词典。 – 2008-12-01 22:19:18

+0

Matt在使用CFMutableDictionary时有一个很好的解决方案,但我使用了一个字典数组。查找需要迭代。它不是最高效的,但速度足够快。 – 2008-12-02 00:10:20

2

一种选择就是对自己继承NSURLConnection的,并添加 - 标签或类似的方法。 NSURLConnection的设计是故意非常裸露的骨头,所以这是完全可以接受的。

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

5

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

1

正如其他答案指出的,你应该在某处存储connectionInfo并通过连接查找它们。

这种最自然的数据类型是NSMutableDictionary,但它不能接受NSURLConnection作为键,因为连接是不可复制的。

使用NSURLConnectionsNSMutableDictionary键另一种选择是使用NSValue valueWithNonretainedObject]

NSMutableDictionary* dict = [NSMutableDictionary dictionary]; 
NSValue *key = [NSValue valueWithNonretainedObject:aConnection] 
/* store: */ 
[dict setObject:connInfo forKey:key]; 
/* lookup: */ 
[dict objectForKey:key]; 
8

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

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

我为setObject:forKey选择的对象是唯一的URL,用于启动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]]; 
19

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


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

这也使得在需要的时候我取消某个名字的连接。

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 
2

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

不需要跟踪的连接,因为在响应返回完成处理器。

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作为参数在无参数块中需要它的参数

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