다중 비동기 NsurlConnection 연결 관리
-
22-07-2019 - |
문제
수업에는 다음과 같은 것처럼 보이는 반복 코드가 많이 있습니다.
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
비동기 요청의 문제점은 다양한 요청이 꺼지는 경우, 하나의 엔티티로 처리하도록 대의원이 할당되어 있으며, 많은 분기 및 못생긴 코드가 공식화되기 시작합니다.
우리는 어떤 종류의 데이터를 되찾고 있습니까? 이것이 포함되어 있다면, 그렇지 않으면 다른 일을하십시오. ID로 뷰를 태그 할 수있는 것처럼 이러한 비동기 요청을 태그 할 수 있다고 생각합니다.
여러 비동기 요청을 처리하는 클래스를 관리하는 데 가장 효율적인 전략이 궁금했습니다.
해결책
나는 그것과 관련된 nsurlconnection에 의해 키가있는 cfmutabledictionaryref에서 응답을 추적합니다. 즉:
connectionToInfoMapping =
CFDictionaryCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
nsmutabledictionary 대신에 이것을 사용하는 것은 이상하게 보일 수 있지만,이 cfdictionary는 키 (nsurlconnection) 만 유지하는 반면 nsdictionary는 키를 복사하고 nsurlconnection은 복사를 지원하지 않기 때문에 그렇게합니다.
완료되면 :
CFDictionaryAddValue(
connectionToInfoMapping,
connection,
[NSMutableDictionary
dictionaryWithObject:[NSMutableData data]
forKey:@"receivedData"]);
그리고 이제 연결에 대한 정보를 추적하는 데 사용할 수있는 각 연결에 대한 "정보"사전과 "Info"사전에는 이미 회신 데이터를 저장하는 데 사용할 수있는 변이 가능한 데이터 객체가 포함되어 있습니다.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSMutableDictionary *connectionInfo =
CFDictionaryGetValue(connectionToInfoMapping, connection);
[[connectionInfo objectForKey:@"receivedData"] appendData:data];
}
다른 팁
두 개의 뚜렷한 nsurlconnections가있는 프로젝트가 있고 동일한 대의원을 사용하고 싶었습니다. 내가 한 일은 수업에서 각 연결 당 하나의 두 가지 속성을 만드는 것이 었습니다. 그런 다음 대표 방법에서 어떤 연결이든지 확인합니다.
- (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];
}
그게 다야.
더 나아가려면 몇 줄의 코드 라인만으로 콜백 역할을하기 위해 블록을 추가 할 수 있습니다.
// 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();
}
블록을 확장하여 매개 변수를 받아들이거나 데이터 릴 연결을 표시하지 않는 방법으로 인수로 전달할 수 있습니다.
이것은 새로운 대답이 아닙니다. 제가 어떻게했는지 보여 드리겠습니다
동일한 클래스의 대의원 메소드 내에서 다른 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]];
내가 취한 방법 중 하나는 각 연결에 대해 대의원과 동일한 개체를 사용하지 않는 것입니다. 대신, 발사 된 각 연결에 대해 새 구문 분석 클래스의 새 인스턴스를 생성하고 대의원을 해당 인스턴스로 설정합니다.
내 커스텀 클래스를 시도하고 다중 공장,이 모든 것을 당신을 위해 처리합니다.
나는 보통 사전 배열을 만듭니다. 각 사전에는 약간의 식별 정보, 응답을 저장하기위한 NSMutabledata 객체 및 연결 자체가 있습니다. 연결 대표 메서드가 발사되면 연결 사전을 찾아서 그에 따라 처리합니다.
한 가지 옵션은 NsurlConnection을 서브 클래스하고 -TAG 또는 유사한 방법을 추가하는 것입니다. nsurlconnection의 디자인은 의도적으로 매우 뼈가 매우 뼈 가므로 완벽하게 허용됩니다.
또는 연결 데이터를 작성하고 수집하는 MyurlConnectionController 클래스를 만들 수도 있습니다. 그런 다음로드가 완료되면 주 컨트롤러 객체를 알리면됩니다.
iOS5 이상에서는 클래스 메소드 만 사용할 수 있습니다.sendAsynchronousRequest:queue:completionHandler:
완료 핸들러에서 응답이 반환되기 때문에 연결을 추적 할 필요가 없습니다.
좋아요 Asihttprequest.
다른 답변에서 지적했듯이 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];
나는 nsurlconnection을 서브 클래스로 결정하고 태그, 대표 및 nsmutabaledata를 추가하기로 결정했습니다. 요청을 포함하여 모든 데이터 관리를 처리하는 데이터 컨트롤러 클래스가 있습니다. DatacontrollerDelegate 프로토콜을 만들어 개별보기/객체가 데이터 콘트롤러를 듣고 요청이 완료되었는지, 그리고 필요한 경우 다운로드 또는 오류가 필요한 경우를 찾을 수 있습니다. DataController 클래스는 NSURLConnection 서브 클래스를 사용하여 새 요청을 시작하고 요청이 완료된시기를 알기 위해 Datacontroller를 듣고 자하는 대의원을 저장할 수 있습니다. 이것은 Xcode 4.5.2 및 iOS 6의 내 작동 솔루션입니다.
Datacontroller.h 파일 DatacontrollerDelegate 프로토콜을 선언하는 파일). 데이터 컨트롤러는 또한 싱글 톤입니다.
@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에는 해시 속성이 있으며이 속성으로 모든 것을 차별 할 수 있습니다.
예를 들어 연결 전후에 특정 정보를 맨 테이프해야하므로 요청 관리자에게는이 작업을 수행 할 NSMutabledictionary가 있습니다.
An Example:
// 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];
}