कई अतुल्यकालिक NSURLConnection कनेक्शन का प्रबंधन


88

मेरी कक्षा में एक बार दोहराने वाला कोड है जो निम्न की तरह दिखता है:

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

अतुल्यकालिक अनुरोधों के साथ समस्या यह है कि जब आपके पास विभिन्न अनुरोध बंद हो रहे हैं, और आपके पास एक प्रतिनिधि के रूप में उन सभी को एक इकाई के रूप में निर्दिष्ट करने के लिए सौंपा गया है, तो बहुत सी शाखाएं और बदसूरत कोड बनने शुरू हो जाते हैं:

हम किस तरह के डेटा वापस पा रहे हैं? यदि इसमें यह शामिल है, तो ऐसा करें, अन्य करें। यह उपयोगी होगा मुझे लगता है कि आप इन अतुल्यकालिक अनुरोधों को टैग करने में सक्षम हो सकते हैं, जैसे आप आईडी के साथ विचारों को टैग करने में सक्षम हैं।

मैं उत्सुक था कि एक वर्ग के प्रबंधन के लिए कौन सी रणनीति सबसे अधिक कुशल है जो कई अतुल्यकालिक अनुरोधों को संभालती है।

जवाबों:


77

मैं एक NSMLConnection से जुड़े CFMutableDictionaryRef में प्रतिक्रियाओं को ट्रैक करता हूं। अर्थात:

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

NSMutableDictionary के बजाय इसका उपयोग करना अजीब लग सकता है, लेकिन मैं ऐसा करता हूं क्योंकि यह CF सहकर्मी केवल इसकी कुंजी (NSURLConnection) को बनाए रखता है जबकि NS सहारे इसकी कुंजी (और 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];
}

चूंकि यह संभव है कि दो या अधिक अतुल्यकालिक कनेक्शन एक समय में प्रतिनिधि विधियों में प्रवेश कर सकते हैं, क्या कुछ विशिष्ट है जो सही व्यवहार सुनिश्चित करने के लिए करने की आवश्यकता होगी?
प्लेगहैमर जैम

(मैंने यह पूछते हुए एक नया प्रश्न बनाया है: stackoverflow.com/questions/1192294/… )
प्लेगहैमेर

3
यदि प्रतिनिधि को कई थ्रेड से बुलाया जा रहा है तो यह थ्रेड सुरक्षित नहीं है। डेटा संरचनाओं की सुरक्षा के लिए आपको पारस्परिक बहिष्करण तालों का उपयोग करना चाहिए। एक बेहतर समाधान NSURLConnection को उपवर्गित कर रहा है और उदाहरण चर के रूप में प्रतिक्रिया और डेटा संदर्भ जोड़ रहा है। मैं नोक्टर्न के सवाल पर इसे स्पष्ट करने वाला एक अधिक विस्तृत उत्तर प्रदान कर रहा हूं: stackoverflow.com/questions/1192294/…
जेम्स वाल्ड

4
Aldi ... यह है onThread: withObject: waitUntilDone :) धागा सुरक्षित आप एक ही धागे (जो आप अपने प्रारंभ कनेक्शन performSelector का उपयोग कर विधि लागू द्वारा आसानी से कर सकते से सभी कनेक्शनों शुरू प्रदान की है। यदि आप कतार के अधिकतम समवर्ती संचालन की तुलना में अधिक कनेक्शन शुरू करने का प्रयास करते हैं (ऑपरेशन समवर्ती चलाने के बजाय कतारबद्ध हो जाते हैं) तो सभी कनेक्शनों को एक NSOperationQueue में रखने से अलग समस्याएं होती हैं। NSOperationQueue सीपीयू बाउंड ऑपरेशंस के लिए अच्छी तरह से काम करता है, लेकिन नेटवर्क बाउंड ऑपरेशंस के लिए, आप उस एप्रोच का उपयोग करना बेहतर समझते हैं जो एक निश्चित आकार के पूल का उपयोग नहीं करता है।
मैट गैलाघेर

1
बस यह साझा करना चाहता था कि iOS 6.0 और इसके बाद के संस्करण के लिए, आप [NSMapTable weakToStrongObjectsMapTable]इसके बजाय उपयोग कर सकते हैं CFMutableDictionaryRefऔर परेशानी को बचा सकते हैं । मेरे लिए अच्छा काम किया।
शाए अविव

19

मेरे पास एक परियोजना है जहां मेरे पास दो अलग-अलग NSURLConnections हैं, और एक ही प्रतिनिधि का उपयोग करना चाहते हैं। मैंने जो कुछ किया वह मेरी कक्षा में दो गुण थे, प्रत्येक कनेक्शन के लिए एक। फिर प्रतिनिधि विधि में, मैं यह देखने के लिए जाँच करता हूं कि यह कौन सा कनेक्शन है


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

यह मुझे जरूरत पड़ने पर नाम से एक विशिष्ट कनेक्शन को रद्द करने की भी अनुमति देता है।


सावधान इस समस्याग्रस्त है के रूप में यह दौड़ की स्थिति होगा हो
adit

आप पहली बार में प्रत्येक कनेक्शन के लिए नाम (saveConnection और sharingReturnedData) कैसे असाइन करते हैं?
jsherk

@adit, नहीं, इस कोड में निहित कोई दौड़ की स्थिति नहीं है। आपको रेस की स्थिति बनाने के लिए कनेक्शन निर्माण कोड के साथ अपने रास्ते से बहुत दूर जाना होगा
माइक अब्दुल्ला

आपका 'समाधान' वास्तव में वही है जो मूल प्रश्न को ऊपर से उद्धृत करने से बचने के लिए देख रहा है: '... बहुत सारे ब्रांचिंग और बदसूरत कोड तैयार होने लगते हैं ...'
stefanB

1
@adit यह एक दौड़ की स्थिति क्यों पैदा करेगा? यह मेरे लिए एक नई अवधारणा है।
गुप्त ५:१३

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 को उस विधि के तर्क के रूप में पास कर सकते हैं, जिसे नो-आर्ग्स ब्लॉक में दिखाया गया है।


यह एक शानदार जवाब है जिसने मेरे मामले के लिए वास्तव में अच्छा काम किया है। बहुत सरल और साफ!
18

8

यह एक नया उत्तर नहीं है। कृपया मुझे पता है कि तुम मुझे कैसे पता चलेगा

एक ही वर्ग के प्रतिनिधि तरीकों के भीतर अलग-अलग NSURLConnection को अलग करने के लिए, मैं NSMLCableection का उपयोग करता हूं, 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]];

5

एक दृष्टिकोण जो मैंने लिया है वह प्रत्येक कनेक्शन के लिए प्रतिनिधि के रूप में एक ही वस्तु का उपयोग नहीं करना है। इसके बजाय, मैं प्रत्येक कनेक्शन के लिए अपने पार्सिंग वर्ग का एक नया उदाहरण बनाता हूं जिसे निकाल दिया जाता है और प्रतिनिधि को उस उदाहरण पर सेट करता है।


एक कनेक्शन के संबंध में बहुत बेहतर एनकैप्सुलेशन।
केदार परांजपे


2

मैं आमतौर पर शब्दकोशों की एक सरणी बनाता हूं। प्रत्येक शब्दकोश में पहचान की जानकारी, प्रतिक्रिया को संग्रहीत करने के लिए एक NSMutableData वस्तु और स्वयं कनेक्शन का एक सा है। जब एक कनेक्शन प्रतिनिधि विधि आग लगाती है, तो मैं कनेक्शन के शब्दकोश को देखता हूं और तदनुसार इसे संभालता हूं।


बेन, नमूना कोड के एक टुकड़े के लिए आपसे पूछना ठीक रहेगा? मैं कल्पना करने की कोशिश कर रहा हूं कि आप यह कैसे कर रहे हैं, लेकिन यह सब नहीं है।
Coocoo4Cocoa 21

विशेष रूप से बेन में, आप शब्दकोश कैसे देखते हैं? आपके पास शब्दकोशों का शब्दकोश नहीं हो सकता क्योंकि NSURLConnection NSCopying को लागू नहीं करता है (इसलिए इसे कुंजी के रूप में उपयोग नहीं किया जा सकता है)।
एडम अर्न्स्ट

मैट में सीएफमुटेबेल्डर का उपयोग करके नीचे एक उत्कृष्ट समाधान है, लेकिन मैं शब्दकोशों की एक सरणी का उपयोग करता हूं। एक लुकअप को पुनरावृत्ति की आवश्यकता होती है। यह सबसे कुशल नहीं है, लेकिन यह काफी तेज है।
बेन गोटलिब

2

एक विकल्प सिर्फ NSURLConnection को खुद को उप-वर्ग करने और एक -tag या इसी तरह की विधि जोड़ना है। NSURLConnection का डिज़ाइन जानबूझकर बहुत नंगे हड्डियों है इसलिए यह पूरी तरह से स्वीकार्य है।

या शायद आप एक MyURLConnectionController क्लास बना सकते हैं जो कनेक्शन के डेटा को बनाने और इकट्ठा करने के लिए जिम्मेदार है। एक बार लोडिंग समाप्त हो जाने के बाद उसे केवल अपने मुख्य नियंत्रक ऑब्जेक्ट को सूचित करना होगा।


2

iOS5 और इसके बाद के संस्करण में आप क्लास विधि का उपयोग कर सकते हैं sendAsynchronousRequest:queue:completionHandler:

पूर्ण हैंडलर में प्रतिक्रिया लौटने के बाद से कनेक्शन का ट्रैक रखने की आवश्यकता नहीं है।


1

मुझे ASIHTTPRequest पसंद है ।


मुझे वास्तव में ASIHTTPRequest में 'ब्लॉक' कार्यान्वयन पसंद है - यह जावा में बेनामी इनर टाइप्स की तरह है। यह कोड स्वच्छता और संगठन के संदर्भ में अन्य सभी समाधानों को धड़कता है।
मैट लियोन

1

जैसा कि अन्य उत्तरों द्वारा बताया गया है, आपको कनेक्शन कहीं भी स्टोर करना चाहिए और कनेक्शन द्वारा उन्हें देखना चाहिए।

इसके लिए सबसे प्राकृतिक डेटाटाइप है 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];

0

मैंने NSURLConnection को उप-करने और एक टैग, डेलीगेट और एक NSMutabaleData जोड़ने का फैसला किया। मेरे पास एक DataController क्लास है जो अनुरोधों सहित सभी डेटा प्रबंधन को संभालती है। मैंने एक DataControllerDelegate प्रोटोकॉल बनाया, ताकि व्यक्तिगत विचार / वस्तुएं DataController को सुन सकें ताकि यह पता लगाया जा सके कि उनके अनुरोध कब समाप्त हुए थे, और यदि आवश्यक हो तो कितना डाउनलोड या त्रुटियां हुई हैं। DataController वर्ग एक नया अनुरोध शुरू करने के लिए NSURLConnection उपवर्ग का उपयोग कर सकता है, और उस प्रतिनिधि को बचा सकता है जो अनुरोध समाप्त होने पर पता करने के लिए DataController को सुनना चाहता है। यह XCode 4.5.2 और ios 6 में मेरा काम करने वाला समाधान है।

DataController.h फ़ाइल जो DataControllerDelegate प्रोटोकॉल की घोषणा करता है)। 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

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];
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.