2009-11-13 11 views
37

मुझे समस्या मिली है जब मैंने पृष्ठभूमि थ्रेड से सर्वर के लिए एसिंक्रोनस अनुरोध करने का प्रयास किया था। मुझे उन अनुरोधों का नतीजा कभी नहीं मिला है। सरल उदाहरण है जो समस्या से पता चलता: फिरपृष्ठभूमि थ्रेड से सर्वर के लिए असीमित अनुरोध

@protocol AsyncImgRequestDelegate 
-(void) imageDownloadDidFinish:(UIImage*) img; 
@end 


@interface AsyncImgRequest : NSObject 
{ 
NSMutableData* receivedData; 
id<AsyncImgRequestDelegate> delegate; 
} 

@property (nonatomic,retain) id<AsyncImgRequestDelegate> delegate; 

-(void) downloadImage:(NSString*) url ; 

@end 



@implementation AsyncImgRequest 
-(void) downloadImage:(NSString*) url 
{ 
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:url] 
      cachePolicy:NSURLRequestUseProtocolCachePolicy 
      timeoutInterval:20.0]; 
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self]; 
if (theConnection) { 
    receivedData=[[NSMutableData data] retain]; 
} else { 
} 

} 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{ 
    [delegate imageDownloadDidFinish:[UIImage imageWithData:receivedData]]; 
    [connection release]; 
    [receivedData release]; 
} 
@end 

मैं मुख्य थ्रेड से इस फोन

asyncImgRequest = [[AsyncImgRequest alloc] init]; 
asyncImgRequest.delegate = self; 
[self performSelectorInBackground:@selector(downloadImage) withObject:nil]; 

विधि downloadImage नीचे सूचीबद्ध है:

-(void) downloadImage 
{ 
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 
[asyncImgRequest downloadImage:@"http://photography.nationalgeographic.com/staticfiles/NGS/Shared/StaticFiles/Photography/Images/POD/l/leopard-namibia-sw.jpg"]; 
[pool release]; 
} 

समस्या यह है कि विधि imageDownloadDidFinish कभी नहीं कहा जाता है । इसके अलावा विधियों में से कोई भी

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response 

कहा जाता है। लेकिन अगर मैं

[self performSelectorInBackground:@selector(downloadImage) withObject:nil]; 

की जगह द्वारा

[self performSelector:@selector(downloadImage) withObject:nil]; 

सब कुछ सही काम कर रहा है। मुझे लगता है कि एसिंक अनुरोध समाप्त होने से पहले पृष्ठभूमि धागा मर जाता है और यह समस्या का कारण बनता है लेकिन मुझे यकीन नहीं है। क्या मैं इस धारणा के साथ सही हूँ? क्या इस समस्या से बचने का कोई तरीका है?

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

अग्रिम धन्यवाद।

उत्तर

59

हां, धागा बाहर निकल रहा है। आप जोड़कर इस देख सकते हैं:

-(void)threadDone:(NSNotification*)arg 
{ 
    NSLog(@"Thread exiting"); 
} 

[[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(threadDone:) 
              name:NSThreadWillExitNotification 
              object:nil]; 

आप के साथ बाहर निकलने से धागा रख सकते हैं:

-(void) downloadImage 
{ 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 
    [self downloadImage:urlString]; 

    CFRunLoopRun(); // Avoid thread exiting 
    [pool release]; 
} 

हालांकि, इस सूत्र होगा कभी नहीं बाहर निकलने का मतलब है। तो जब आप पूरा कर लेंगे तो आपको इसे रोकना होगा।

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{ 
    CFRunLoopStop(CFRunLoopGetCurrent()); 
} 

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{ 
    CFRunLoopStop(CFRunLoopGetCurrent()); 
} 

Threading Guide और RunLoop Reference में चलाएँ लूप्स के बारे में अधिक जानें।

+0

कैसे एक NSInvocationOperation भीतर एक अतुल्यकालिक NSURLConnection प्रदर्शन करने पर कई घंटे (8) के लिए शोध करने के बाद, समाधान में से कोई भी थे CFRunLoopRun() और CFRunLoopStop() के रूप में सुरुचिपूर्ण के रूप में। केवीओ प्रोटोकॉल का उपयोग करते हुए "isExecuting" इंस्टेंस चर के साथ गड़बड़ करने से यह बहुत बेहतर है। ध्यान देने योग्य एक बात यह है कि मैंने @ सेलेक्टर (प्रदर्शन चयनकर्ता ओनमेन थ्रेड: ऑब्जेक्ट: प्रतीक्षा करें नहीं किया: मोड :) पास हो रहा है [एनएसएआरएआरएआरआईआरआईथऑब्जेक्ट: एनएसडीफॉल्टरुन लूपमोड] मोड के रूप में। यूआई इंटरैक्शन चिकनी रखने के लिए UIKit आवश्यकताओं को पूरा करने के लिए मुख्य थ्रेड, NSDefaultRunLoopMode। आपका बहुत बहुत धन्यवाद! –

+0

इच्छा है कि मैं और अधिक वोट दे सकता हूं। यह बेहद सहायक था। –

+6

एआरसी के साथ संकलित परियोजनाओं के लिए, 'NSAutoReleasePool' उपलब्ध नहीं है, इसलिए कोड' @autoreleasepool {[self downloadImage: urlString] जैसा दिखाई देगा; CFRunLoopRun(); } ' – alokoko

0

NSURLRequests वैसे भी पूरी तरह से असीमित हैं। आप मुख्य थ्रेड के अलावा किसी अन्य धागे से एक NSURLRequest बनाने की जरूरत है, तो मुझे लगता है कि यह करने के लिए सबसे अच्छा तरीका है बस मुख्य थ्रेड से NSURLRequestबनाते हैं।

// Code running on _not the main thread_: 
[self performSelectorOnMainThread:@selector(SomeSelectorThatMakesNSURLRequest) 
     withObject:nil 
     waitUntilDone:FALSE] ; // DON'T block this thread until the selector completes. 

यह सब करता है मुख्य थ्रेड से HTTP अनुरोध बंद गोली मार है (इतना है कि यह वास्तव में काम करता है और रहस्यमय तरीके से गायब हो जाते हैं नहीं है)। HTTP प्रतिक्रिया सामान्य रूप से कॉलबैक में वापस आ जाएगी।

आप GCD के साथ ऐसा करना चाहते हैं, तो आप सिर्फ

// From NOT the main thread: 
dispatch_async(dispatch_get_main_queue(), ^{ // 
    // Perform your HTTP request (this runs on the main thread) 
}) ; 

MAIN_QUEUE रन मुख्य थ्रेड पर जा सकते हैं।

तो मेरी HTTP समारोह पाने की पहली पंक्ति लगता है:

void Server::get(string queryString, function<void (char*resp, int len) > onSuccess, 
        function<void (char*resp, int len) > onFail) 
{ 
    if(![NSThread isMainThread]) 
    { 
     warning("You are issuing an HTTP request on NOT the main thread. " 
       "This is a problem because if your thread exits too early, " 
       "I will be terminated and my delegates won't run") ; 

     // From NOT the main thread: 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      // Perform your HTTP request (this runs on the main thread) 
      get(queryString, onSuccess, onFail) ; // re-issue the same HTTP request, 
      // but on the main thread. 
     }) ; 

     return ; 
    } 
    // proceed with HTTP request normally 
} 
1

आप एक पृष्ठभूमि धागे पर कनेक्शन शुरू कर सकते हैं, लेकिन आप प्रतिनिधि तरीकों मुख्य थ्रेड पर कहा जाता है यह सुनिश्चित करने के लिए है। यह

[[NSURLConnection alloc] initWithRequest:urlRequest 
           delegate:self]; 

के साथ तुरंत शुरू नहीं किया जा सकता है।

क्या यह प्रतिनिधि कतार कॉन्फ़िगर करने के लिए है और यह भी माध्यमिक धागे पर काम करता है:

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:urlRequest 
                   delegate:self 
                 startImmediately:NO]; 
[connection setDelegateQueue:[NSOperationQueue mainQueue]]; 
[connection start]; 
संबंधित मुद्दे