একাধিক অ্যাসিনক্রোনাস এনএসআরএল সংযোগ সংযোগ পরিচালনা করা


88

আমার ক্লাসে আমার কাছে একটি টন পুনরাবৃত্তি কোড রয়েছে যা নীচের মত দেখাচ্ছে:

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

অ্যাসিক্রোনাস অনুরোধগুলির সাথে সমস্যাটি যখন আপনার বিভিন্ন অনুরোধগুলি বন্ধ হয়ে যায়, এবং আপনার একটি প্রতিনিধি নিয়োগ করা হয় সেগুলি সমস্তকে একটি সত্তা হিসাবে বিবেচনা করার জন্য, প্রচুর শাখা এবং কুরুচিপূর্ণ কোডটি চালু করা শুরু করে:

আমরা কী ধরণের তথ্য ফিরে পাচ্ছি? যদি এটিতে এটি থাকে তবে এটি করুন, অন্যথায় করুন। আপনারা আইডি দিয়ে ভিউগুলিতে ট্যাগ করতে সক্ষম হচ্ছেন এমন ধরনের এই অ্যাসিনক্রোনাস অনুরোধগুলিতে ট্যাগ করতে সক্ষম হবেন বলে আমি মনে করি এটি দরকারী হবে।

আমি আগ্রহী ছিলাম যে ক্লাসটি একাধিক অ্যাসিনক্রোনাস অনুরোধগুলি পরিচালনা করে তার পরিচালনা করার জন্য কোন কৌশলটি সবচেয়ে দক্ষ।

উত্তর:


77

আমি এর সাথে যুক্ত এনএসআরএল সংযোগ দ্বারা কীবোর্ডযুক্ত একটি সিএফ মিউটেবল ডিকোরিয়ান্স রিফগুলিতে প্রতিক্রিয়াগুলি ট্র্যাক করি। অর্থাত:

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

এটি এনএসমিটেবলড অভিধানের পরিবর্তে এটি ব্যবহার করা অদ্ভুত বলে মনে হতে পারে তবে আমি এটি করি কারণ এই সিএফডি অভিধানটি কেবল তার কীগুলি ধরে রাখে (এনএসআরএল সংযোগ) যেখানে এনএসডেটরিয় তার কীগুলি অনুলিপি করে (এবং এনএসআরএল সংযোগ অনুলিপি সমর্থন করে না)।

একবার হয়ে গেলে:

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/... )
Debajit

4
যদি প্রতিনিধিটিকে একাধিক থ্রেড থেকে কল করা হয় তবে এটি থ্রেড নিরাপদ নয়। ডেটা কাঠামো রক্ষা করতে আপনাকে অবশ্যই পারস্পরিক বর্জনীয় লকগুলি ব্যবহার করতে হবে। আরও ভাল সমাধান হ'ল এনএসআরএল সংযোগ সাবক্ল্যাসিং করা এবং প্রতিক্রিয়া এবং ডেটা রেফারেন্সকে উদাহরণ ভেরিয়েবল হিসাবে যুক্ত করা। নোক্টুর্নের
জেমস ওয়াল্ড

4
অ্যালডি ... এটা হল onThread: withObject: waitUntilDone :) থ্রেড নিরাপদ আপনি একই থ্রেড (যা আপনি আপনার শুরুর সংযোগ performSelector ব্যবহার পদ্ধতি আবাহন করার মাধ্যমে সহজেই করতে পারেন থেকে সমস্ত সংযোগ শুরু করে। যদি আপনি সারির সর্বাধিক যুগ্ম ক্রিয়াকলাপের চেয়ে বেশি সংযোগ শুরু করার চেষ্টা করেন (একযোগে চলার পরিবর্তে ক্রিয়াকলাপগুলি সারিবদ্ধ হয়ে যায়) তবে সমস্ত সংযোগ একটি এনএসওরেশনকিউতে রাখার ক্ষেত্রে বিভিন্ন সমস্যা রয়েছে। NSOperationQueue সিপিইউ বাউন্ড অপারেশনগুলির জন্য ভাল কাজ করে তবে নেটওয়ার্ক বাউন্ড অপারেশনগুলির জন্য, আপনি এমন একটি পদ্ধতির ব্যবহার করা ভাল যা নির্দিষ্ট আকারের থ্রেড পুল ব্যবহার করে না।
ম্যাট গ্যালাগার

4
কেবল এটি শেয়ার করতে চেয়েছিলেন আইওএস 6.0 এবং তারপরের জন্য, আপনি এগুলির [NSMapTable weakToStrongObjectsMapTable]পরিবর্তে একটি ব্যবহার করতে পারেন CFMutableDictionaryRefএবং ঝামেলা বাঁচাতে পারেন। আমার জন্য ভাল কাজ করেছে।
শাই আভিভ

19

আমার একটি প্রকল্প রয়েছে যেখানে আমার দুটি স্বতন্ত্র এনএসআরএল সংযোগ রয়েছে এবং একই প্রতিনিধিটি ব্যবহার করতে চেয়েছিলাম। আমি যা করেছি তা ছিল আমার ক্লাসে দুটি বৈশিষ্ট্য তৈরি করা, প্রতিটি সংযোগের জন্য একটি। তারপরে ডেলিগেট পদ্ধতিতে আমি এটি পরীক্ষা করে দেখি যে এটি কোন সংযোগ


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

এটি প্রয়োজন অনুসারে নাম দ্বারা একটি নির্দিষ্ট সংযোগ বাতিল করার অনুমতি দেয়।


সাবধান থাকুন এটি সমস্যাজনক কারণ এর রেসের শর্ত থাকবে
এডিট

আপনি প্রথম স্থানটিতে প্রতিটি সংযোগের জন্য কীভাবে নামগুলি (সেভিং কানেকশন এবং শেয়ারিং রিটার্নডাটাটা) অর্পণ করবেন?
jsherk

@ অ্যাডিট, না, এই কোডটির অন্তর্গত কোনও রেসের শর্ত নেই। রেসের শর্ত তৈরি করতে আপনাকে সংযোগ তৈরির কোডের সাথে আপনার পথ থেকে অনেক দূরে যেতে হবে
মাইক আব্দুল্লাহ

আপনার 'সমাধান' হ'ল মূল প্রশ্নটি এড়াতে
চাইছে

4
@ অ্যাডিট কেন এটি রেসের শর্তের দিকে পরিচালিত করবে? এটি আমার কাছে নতুন ধারণা।
abc123

16

ডেটা ধরে রাখার জন্য সাবস্ক্লাসিং এনএসআরএল সংযোগ পরিষ্কার, অন্যান্য উত্তরগুলির তুলনায় কম কোড, আরও নমনীয়, এবং রেফারেন্স পরিচালনা সম্পর্কে কম চিন্তা প্রয়োজন।

// 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

আপনি এনএসআরএল সংযোগের মতো এটি ব্যবহার করুন এবং এর ডেটা সম্পত্তিটিতে ডেটা সংগ্রহ করবেন:

- (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();
}

প্যারামিটারগুলি গ্রহণ করার জন্য আপনি অবরুদ্ধটিকে প্রসারিত করতে পারেন বা দেখানো হয়েছে এমন কোনও পদ্ধতির জন্য এটির প্রয়োজন হিসাবে এমন কোনও পদ্ধতির আর্গুমেন্ট হিসাবে ডেটা URL সংযোগটি পাস করতে পারেন


এটি একটি দুর্দান্ত উত্তর যা আমার ক্ষেত্রে সত্যই কার্যকর হয়েছে। খুব সাধারণ এবং পরিষ্কার!
jwarrent

8

এটি কোনও নতুন উত্তর নয়। দয়া করে আমাকে কীভাবে আপনি রেখেছিলেন তা দেখান

একই শ্রেণীর প্রতিনিধি পদ্ধতির মধ্যে বিভিন্ন এনএসআরএল সংযোগের পার্থক্য করার জন্য, আমি এনএসআরএল সংযোগটি (NSString *)descriptionকী হিসাবে ব্যবহার করে সেট এবং অপসারণ করতে NSMutableD অভিধান ব্যবহার করি ।

বস্তু আমি বেছে নেওয়া হয়েছে setObject:forKeyঅনন্য URL রয়েছে যা সূচনা জন্য ব্যবহার করা হয় NSURLRequest, NSURLConnectionব্যবহারসমূহ।

একবার সেট করা NSURL সংযোগ এ মূল্যায়ন করা হয়

-(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

আমি সাধারণত অভিধানের একটি অ্যারে তৈরি করি। প্রতিটি অভিধানে কিছু শনাক্ত করার তথ্য রয়েছে, প্রতিক্রিয়া সংরক্ষণ করার জন্য একটি এনএসউটেবল ডেটা অবজেক্ট এবং সংযোগটি নিজেই রয়েছে। যখন কোনও সংযোগ প্রতিনিধি পদ্ধতিটি জ্বলতে থাকে আমি সংযোগটির অভিধানটি সন্ধান করি এবং সেই অনুযায়ী এটি পরিচালনা করি।


বেন, নমুনা কোডটির একটি অংশ জিজ্ঞাসা করা কি ঠিক হবে? আমি কীভাবে আপনি এটি করছেন তা কল্পনা করার চেষ্টা করছি, তবে এটি সেখানে নেই।
Coocoo4Cocoa

বিশেষত বেন, আপনি অভিধানটি কীভাবে সন্ধান করবেন? আপনার কাছে অভিধানের একটি অভিধান থাকতে পারে না যেহেতু এনএসআরএল সংযোগটি এনএসকোভিং প্রয়োগ করে না (তাই এটি কী হিসাবে ব্যবহার করা যায় না)।
অ্যাডাম আর্নস্ট

ম্যাট সিএফএমটেবলড অভিধান ব্যবহার করে নীচে একটি দুর্দান্ত সমাধান আছে, তবে আমি অভিধানের একটি অ্যারে ব্যবহার করি। একটি অনুসন্ধানের একটি পুনরাবৃত্তি প্রয়োজন। এটি সবচেয়ে দক্ষ নয়, তবে এটি যথেষ্ট দ্রুত।
বেন গটলিব 0

2

একটি বিকল্প হ'ল এনএসআরএল সংযোগ নিজেই সাবক্লাস করুন এবং একটি-ট্যাগ বা অনুরূপ পদ্ধতি যুক্ত করুন। এনএসআরএল সংযোগের নকশা ইচ্ছাকৃতভাবে খুব খালি হাড়ের তাই এটি পুরোপুরি গ্রহণযোগ্য।

অথবা সম্ভবত আপনি কোনও মাইআরএল সংযোগ নিয়ন্ত্রণকারী শ্রেণি তৈরি করতে পারেন যা সংযোগের ডেটা তৈরি এবং সংগ্রহের জন্য দায়ী। লোডিং শেষ হয়ে গেলে এটি কেবলমাত্র আপনার প্রধান নিয়ামক অবহিতকে জানাতে হবে।


2

আইওএস 5 এবং এর উপরে আপনি কেবল শ্রেণির পদ্ধতিটি ব্যবহার করতে পারেন 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

আমি এনএসআরএল সংযোগ সাবক্লাস করার সিদ্ধান্ত নিয়েছি এবং একটি ট্যাগ, প্রতিনিধি এবং একটি এনএসমুতাবালা ডেটা যুক্ত করব। আমার কাছে একটি ডেটা কন্ট্রোলার ক্লাস রয়েছে যা অনুরোধগুলি সহ সমস্ত ডেটা ম্যানেজমেন্ট পরিচালনা করে। আমি একটি ডেটা কন্ট্রোলারডেলিগেট প্রোটোকল তৈরি করেছি, যাতে স্বতন্ত্র ভিউ / অবজেক্টগুলি তাদের অনুরোধগুলি কখন শেষ হয়ে যায়, এবং যদি প্রয়োজন হয় তবে ডাউনলোড হয়েছে বা ত্রুটি হয়েছে কিনা তা জানতে ডেটা কন্ট্রোলার শুনতে শুনতে পারে। ডেটা কনট্রোলার শ্রেণি একটি নতুন অনুরোধ শুরু করতে এনএসআরএল সংযোগ সাবক্লাস ব্যবহার করতে পারে এবং অনুরোধটি কখন শেষ হয়ে যায় তা জানতে ডেটা কন্ট্রোলার শুনতে চাইলে যে প্রতিনিধি সংরক্ষণ করতে পারে save এটি এক্সকোড ৪.৪.২ এবং আইওএস my এ আমার কাজের সমাধান।

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

এনএসআরএল সংযোগটি উইথডেলিগেট এইচ: @ প্রোটোকল ডেটা কন্ট্রোলারডেলিগেট;

@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

এবং এনএসআরএল সংযোগবিহীন ডেলিগেট.এম:

#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

প্রতিটি এনএসআরএল সংযোগের একটি হ্যাশ বৈশিষ্ট্য রয়েছে, আপনি এই বৈশিষ্ট্যটি দ্বারা বৈষম্য করতে পারেন।

উদাহরণস্বরূপ, সংযোগের আগে এবং পরে আমার কিছু নির্দিষ্ট তথ্য মন্টেন করা দরকার, সুতরাং এটি করার জন্য আমার রিকোয়েস্টম্যানেজারের একটি এনএস পরিবর্তনযোগ্য অভিধান রয়েছে।

একটি উদাহরণ:

// 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];
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.