2009-11-13 25 views
37

Arka plan iş parçacığından sunucuya eşzamansız istekleri denediğimde sorun yaşıyorum. Bu isteklerin hiçbir zaman sonuçlarını almadım. Sorunu gösteren basit bir örnek: SonraArka plan iş parçacığından sunucuya eşzamanlı istek

@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 

ben ana iş parçacığı bu çağrı

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

yöntem downloadImage aşağıda listelenmiştir:

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

sorun bu yöntem imageDownloadDidFinish asla denir olduğu . yöntemlerle

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

ait Üstelik hiçbiri denir. Ancak

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

her şey tarafından

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

değiştirirseniz doğru çalışıyor. Async isteğinin iş bittiğinden önce arka plan iş parçacığının öldüğünü varsayar ve bu da soruna neden olur, ancak emin değilim. Bu varsayımlarda haklı mıyım? Bu problemden sakınmanın bir yolu var mı?

Bu sorunu önlemek için eşitleme isteğini kullanabileceğimi biliyorum, ancak bu basit bir örnektir, gerçek durum daha karmaşıktır.

Şimdiden teşekkürler.

cevap

59

Evet, iş parçacığı çıkıyor. Sen ekleyerek bu görebilirsiniz:

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

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

Sen ile çıkmasını iplik tutabilir:

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

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

Ancak bu iplik çıkış asla anlamına gelir. İşin bittiğinde durman gerek.

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

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

Threading Guide ve RunLoop Reference Run Loops hakkında daha fazla bilgi.

+0

, çözümlerin hiçbiri vardı CFRunLoopRun() ve CFRunLoopStop() kadar zarif. Bu, KVO protokolünü kullanarak "isExecuting" örnek değişkenleriyle uğraşmaktan daha iyidir. Unutulmaması gereken bir şey, @selector (performSelectorOnMainThread: withObject: waitUntilDone: modes :) modlarını [NSArray arrayWithObject: NSDefaultRunLoopMode] modları olarak kullanıyorum. UIKit gereksinimlerini karşılamak için ana iş parçacığı, UI etkileşimini pürüzsüz tutmak için NSDefaultRunLoopMode. Çok teşekkürler! –

+0

Keşke daha fazla oy verebilirdim. Bu son derece yararlı oldu. –

+6

ARC ile derlenen projeler için, 'NSAutoReleasePool' kullanılamaz, bu nedenle kod, '@autoreleasepool {gibi görünüyordu [self downloadImage: urlString]; CFRunLoopRun(); } ' – alokoko

0

NSURLRequest'ler zaten tamamen eşzamansızdır. Eğer ana iş parçacığı dışında başka bir iş parçacığı bir NSURLRequest yapmanız gerekiyorsa, bunu yapmanın en iyi yolu sadece ana iş parçacığı den NSURLRequestyapmak olduğunu düşünüyorum. Bütün bunlar yapar

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

ana iş parçacığı gelen HTTP isteği fırlamak (bu kapalı gerçekten çalışıyor ve gizemli kaybolmasına engel). HTTP yanıtı her zamanki gibi geri aramalara geri dönecektir. Eğer GCD ile bunu istiyorsanız

, sadece ana iş parçacığı üzerinde

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

MAIN_QUEUE çalışır gidebilir.Bir arka plan iş parçacığı üzerinde bağlantıyı başlayabilir ama yöntemler ana iş parçacığı üzerinde denir temsilci sağlamak zorunda

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

: gibi

Yani benim HTTP fonksiyonu olsun ilk satırı görünüyor. o hemen başladığından bu

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

ile yapılamaz.

temsilci kuyruğunu yapılandırmak için bunu yapın ve hatta ikincil iş parçacığı üzerinde çalışır: Bir NSInvocationOperation içinde uyumsuz bir NSURLConnection nasıl yapılacağına ilişkin saatlerce (8+) için araştırma yaptıktan sonra

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:urlRequest 
                   delegate:self 
                 startImmediately:NO]; 
[connection setDelegateQueue:[NSOperationQueue mainQueue]]; 
[connection start]; 
İlgili konular