2011-06-27 9 views
7

メソッドを起動してバックグラウンドで実行したい - 起動後に実際に何が起きても気にしません。dispatch_queue_tはまだメインスレッドをブロックしています

は、だから私は私の通常のコードのすべてとこれを持って私のメインviewDidLoadMethodに:

dispatch_queue_t newImages = dispatch_queue_create("load image in background", NULL); 
    dispatch_async(newImages, ^{ 
     [self getNewImages]; 
    }); 
    dispatch_release(newImages); 

私の仮定は、キューが作成されると、その関数呼び出しがバックグラウンドスレッドで実行するように設定されるだろうということでしたし、私のアプリは一緒に巡航し続けるだろう。それは事実ではないようです。

この関数から呼び出されたものはすべてバックグラウンドスレッドに自動的に移動されるか、他のブロック呼び出しが発生していないことを確認する必要がありますか?

EDIT - ブロックしているCODE:

-(void) getNewImages 
{ 
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString *documentsPath = [paths objectAtIndex:0]; 
    NSString *lastImagesSyncDate = [defaults valueForKey:@"ImagesLastSyncDate"]; 

    dispatch_queue_t newImages = dispatch_queue_create("com.mydomain.app.newimagesinbackground", NULL); 
    dispatch_async(newImages, ^{ 

     for (Manufacturer *m in self.manufacturers) 
     { 
      NSString *myurl = [NSString stringWithFormat: kNewImagesURL, m.ManufacturerID ,lastImagesSyncDate]; 
      NSString *manufacturerID = [m.ManufacturerID stringValue]; 
      NSURL *url = [NSURL URLWithString:[myurl stringByReplacingOccurrencesOfString:@" " withString:@"%20"]]; 
      __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 
      [request setCompletionBlock:^{ 

       // Use when fetching text data 
       NSString *responseString = [request responseString]; 
       NSString *fileName =[documentsPath stringByAppendingPathComponent: [NSString stringWithFormat:@"newimages%@.plist", manufacturerID]]; 
       [responseString writeToFile:fileName atomically:YES encoding:NSUTF8StringEncoding error:nil]; 
       NSArray *array = [[NSArray alloc] initWithContentsOfFile:fileName]; 

       for (NSDictionary* dict in array) { 

        NSString *fileName = [NSString stringWithFormat:@"%@/%@_tn.jpg?t=",manufacturerID, [[dict valueForKey:@"ItemID"] stringByReplacingOccurrencesOfString:@" " withString:@"%20"]]; 
        NSString *savePath = [documentsPath stringByAppendingPathComponent:fileName]; 
        NSURL *url = [NSURL URLWithString: [[NSString stringWithFormat:kProductImagesURL, fileName]stringByAppendingString:lastImagesSyncDate]]; 

        __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 
        [request setCompletionBlock:^{ 

         int statusCode = [request responseStatusCode]; 
         if (statusCode==200) { 
          NSData *responseData = [request responseData]; 
          [responseData writeToFile:[savePath stringByReplacingOccurrencesOfString:@"%20" withString:@" "] atomically:YES]; 
         } 

        }]; 
        [request setFailedBlock:^{ 
         // NSError *error = [request error]; 
        }]; 
        [request startAsynchronous]; 
       } 

       [array release];  
      }]; 
      [request setFailedBlock:^{ 
       // NSError *error = [request error]; 
      }]; 
      [request startAsynchronous]; 
     } 

     dispatch_async(dispatch_get_main_queue(), ^{ 
      dispatch_release(newImages); //this executes on main thread 
     }); 
    }); 
} 

私は、彼らが完了するまで、それは一度に1を実行しているに追加され、これらの呼び出しの全てをここに作成された唯一の1キューがあることを前提としています。私はこれのいくつかのバリエーションを試して、それは研削停止に私のアプリをドラッグ - それはクラッシュしないが、メインスレッドがクロールしています。

更新日: 私のキュー内の非同期ASIHTTPRequestをすべてスレッド内に作成していたので、コードを更新してこのコードの長時間実行部分を代わりに、同期要求をキューして使用します。

for (NSDictionary* dict in array) { 
       dispatch_async(newImages, ^{ 
        NSString *fileName = [NSString stringWithFormat:@"%@/%@_tn.jpg?t=",manufacturerID, [[dict valueForKey:@"ItemID"] stringByReplacingOccurrencesOfString:@" " withString:@"%20"]]; 
        NSString *savePath = [documentsPath stringByAppendingPathComponent:fileName]; 
        NSURL *url = [NSURL URLWithString: [[NSString stringWithFormat:kProductImagesURL, fileName]stringByAppendingString:lastImagesSyncDate]]; 

        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 
        [request startSynchronous]; 
        NSError *error = [request error]; 
        if (!error) { 
         int statusCode = [request responseStatusCode]; 
         if (statusCode==200) { 
          NSData *responseData = [request responseData]; 
          [responseData writeToFile:[savePath stringByReplacingOccurrencesOfString:@"%20" withString:@" "] atomically:YES]; 
         } 
        } 
       }); 
} 

を今私は再びキューを解放する場所を把握する必要があり、私は運でいくつかのバリエーションを試してみました。

答えて

3

私は私のために完璧に働いているものである周りの長い道のりを行ってきましたが、最終的にここに私の頭の中の概念を持って:

dispatch_queue_t newImages = dispatch_queue_create("com.mydomain.app.newimagesinbackground", NULL); // create my serial queue 
     dispatch_async(newImages, ^{ 
    [self getNewImages]; // call my function - this get added first in my serial queue 
    }); 


    dispatch_async(newImages, ^{ 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      // add this to the main queue as the last item in my serial queue 
      // when I get to this point I know everything in my queue has been run 
      dispatch_release(newImages); 
     }); 
    }); 

私の主な問題はASIHTTPRequest startAsynchronous方法使用していました: http://allseeing-i.com/ASIHTTPRequest/How-to-use#using_blocks

それは本質的に私のボトルネックを作成していました.2000の画像を取得し、2000の非同期呼び出しを作成しようとしました。私がすでにバックグラウンドにいる場合、startSynchronousメソッドはうまく動作し、一度に1つの呼び出ししか実行しようとしません。

5

まず、キュー名を「using reverse domain syntax」とする必要があります(例:「com.foo.myapp.backgroundwork」)。これにより、一意性と一貫性が保証されます。

次に、作成したばかりのキューをすぐに解放しようとしています(おそらく何らかの作業をしています)。それをしないでください。作業が完了したら、キューを解放します。再読み込み同時実行した後は(私は自分の薬を飲む)自分自身を導く、私はランタイムが実際にきれいにすることを学んだ:以下は、実際に編集

dispatch_async(newImages, ^{ 
     [self getNewImages]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      dispatch_release(newImages); //this executes on main thread 
     }); 
    }); 

「管理キューメモリ」の下で、Concurrency programming guideに記載されていますバックグラウンドスレッドで0参照カウントに達するdispatch_queueを設定します。はUIをブロックしません。ただちにリリースするのは悪い考えです。代わりに、私が実践した練習に従ってください。あなたのタスクはメインスレッドでクリーンアップされます。だから私はあなたの-getNewImagesメソッドがあなたのUIをブロックする責任があると考えています。あなたはそのメソッドからいくつかのコードを表示できるので、それを排除できますか?

+0

いつこのキューが完了したかを知っていますか?キューが終了した後に呼び出される何らかの委譲を作成する必要がありますか?それはキューの全体の点に関して完全に直観的ではないようです。 – Slee

+0

更新された回答。どのようにしてキューがいつより優れているのかをシステムは知っていますか?キューにはいつでもタスクを追加できます。そのため、キューが使用するリソースを解放する必要があります。リンクされた同時性ガイドを読むことを強くお勧めします。 2倍。 GCDは両足で自分を撃つのがとても簡単です。 – RyanR

+0

ガイドを今読んで、2回:助けてくれてありがとう。 – Slee

1

私の理解では、ASIHTTPRequest startSynchronousにはNSRunLoopが必要であり、GCDシリアルキュースレッドにはNSRunLoopがありません。したがって、動作しません。

あなたのコードはGCDシリアルキューがまったく必要ないようです。私はGCDのグローバルキューがあなたのコードでうまく動作すると思います。例えば、

- (void)getNewImages 
{ 
    dispatch_queue_t queue = 
     dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); 

    /* snip */ 
    for (Manufacturer *m in self.manufacturers) 
    { 
     /* snip */ 
     ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 
     [request setCompletionBlock:^{ 
      /* snip */ 
      dispatch_async(queue, ^{ 
       [responseString writeToFile:fileName atomically:YES encoding:NSUTF8StringEncoding error:nil]; 
      }); 

      for (NSDictionary* dict in array) { 
       /* snip */ 
       ASIHTTPRequest *imageRequest = [ASIHTTPRequest requestWithURL:url]; 
       [imageRequest setCompletionBlock:^{ 
        /* snip */ 
        dispatch_async(queue, ^{ 
         [responseData writeToFile:[savePath stringByReplacingOccurrencesOfString:@"%20" withString:@" "] atomically:YES]; 
        }); 
       }]; 
       /* snip */ 
       [imageRequest startAsynchronous]; 
      } 
      /* snip */ 
     }]; 
     /* snip */ 
     [request startAsynchronous]; 
    } 
} 

getNewImages方法は、メインスレッド上で実行されなければならない、それはメインスレッドのNSRunLoopを必要とします。

+0

それは実際に私の作成したシリアルキュー上でうまくいっている、私はちょうど私のASIHTTPRequestsの周りにNSAutoreleasePoolを置く必要があり、それらをイメージのダウンロード部分のために同期させました。メモリは、全体の3〜4メガのラウンドにとどまっています。 – Slee

関連する問題