2009-08-31 10 views
8

私はちょうど愚かなUIActivityIndi​​catorViewを動作させるために、私のアプリにマルチスレッドを導入しました。さて、アクティビティインジケータはうまくいきますが、今はアプリがクラッシュすることがあり、時にはそうでない場合があります。これを把握する必要がありますが、どこから探し始めるかわからない...一般的なマルチスレッドミスiPhone初心者向け

初心者がiPhoneでマルチスレッド化することがよくある間違いは何ですか?あなたの答えで具体的にしてください。御時間ありがとうございます。

更新日:参考のために問題のある情報源を追加しました。

//--------------------Where the multithreading starts------------------------ 


-(IBAction)processEdits:(id)sender 
{ 
     //Try to disable the UI to prevent user from launching duplicate threads 
    [self.view setUserInteractionEnabled:NO]; 

     //Initialize indicator (delcared in .h) 
    myIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(155, 230, 20, 20)]; 
    myIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite; 
    [self.view addSubview:myIndicator]; 
    [self.view bringSubviewToFront:myIndicator]; 
    [myIndicator startAnimating]; 


    //Prepare and set properties of the NEXT modal view controller to switch to 
    controller = [[EndViewController alloc] initWithNibName:@"EndViewController" bundle:nil]; 

    controller.delegate = self; 

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


} 



//-----------------------------THE THREAD WORK-------------------------------- 


-(IBAction)threadWork:(id)sender{ 

    NSAutoreleasePool * pool; 
    NSString *   status; 

    pool = [[NSAutoreleasePool alloc] init]; 
    assert(pool != nil); 


     //The image processing work that takes time 
    controller.photoImage = [self buildPhoto]; 

    //Stop the UIActivityIndicatorView and launch next modal view 
    [self performSelectorOnMainThread:@selector(stopSpinner:)withObject:nil waitUntilDone:NO]; 

    [pool drain]; 


} 




//-------------------Most of the WORKLOAD called in above thread ------------------------ 



-(UIImage*)buildPhoto 
{ 
    /* 
     This is the work performed in the background thread. Process photos that the user has edited and arrange them into a UIView to be finally flattened out into a new UIImage. Problem: UI usually changes for some reason during this work. 
     */ 

    UIView* photoContainerView = [[UIView alloc] initWithFrame:CGRectMake(0,0,975,1300)]; 
    photoContainerView.backgroundColor = [UIColor whiteColor]; 
    UIImage* purikuraFlattened; 
    int spacerX = 10; 
    int spacerY = 10; 

    switch (myPattern) { 

     case 0: 

      photoContainerView.frame = CGRectMake(0, 0, 320, 427); 
      layoutSingle = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x,photoContainerView.frame.origin.y,320,427)]; 
      [photoContainerView addSubview:layoutSingle]; 
      layoutSingle.image = editPhotoData1; 

      break; 


     case 1: 

      layoutAimg1 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX, photoContainerView.frame.origin.y+spacerY, 427, 320)]; 
      layoutAimg2 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX+427, photoContainerView.frame.origin.y+spacerY, 427, 320)]; 
      layoutAimg3 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX, photoContainerView.frame.origin.y+spacerY+320, 427, 320)]; 
      layoutAimg4 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX+427, photoContainerView.frame.origin.y+spacerY+320, 427, 320)]; 
      layoutAimg5 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX, photoContainerView.frame.origin.y+spacerY+(320*2), 427, 320)]; 
      layoutAimg6 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX+427, photoContainerView.frame.origin.y+spacerY+(320*2), 427, 320)]; 
      layoutAimg7 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX, photoContainerView.frame.origin.y+spacerY+(320*3), 427, 320)]; 
      layoutAimg8 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX+427, photoContainerView.frame.origin.y+spacerY+(320*3), 427, 320)]; 

      [photoContainerView addSubview:layoutAimg1]; 
      [photoContainerView addSubview:layoutAimg2]; 
      [photoContainerView addSubview:layoutAimg3]; 
      [photoContainerView addSubview:layoutAimg4]; 
      [photoContainerView addSubview:layoutAimg5]; 
      [photoContainerView addSubview:layoutAimg6]; 
      [photoContainerView addSubview:layoutAimg7]; 
      [photoContainerView addSubview:layoutAimg8]; 


      if(myShots == 1){ 

      rotPhoto1 = [self rotateImage:editPhotoData1.size:editPhotoData1]; 

       layoutAimg1.image = rotPhoto1; 
       layoutAimg2.image = rotPhoto1; 
       layoutAimg3.image = rotPhoto1; 
       layoutAimg4.image = rotPhoto1; 
       layoutAimg5.image = rotPhoto1; 
       layoutAimg6.image = rotPhoto1; 
       layoutAimg7.image = rotPhoto1; 
       layoutAimg8.image = rotPhoto1; 



      }else if(myShots == 2){ 


      rotPhoto1 = [self rotateImage:editPhotoData1.size: editPhotoData1]; 
      rotPhoto2 = [self rotateImage:editPhotoData2.size: editPhotoData2]; 

       layoutAimg1.image = rotPhoto1; 
       layoutAimg2.image = rotPhoto2; 
       layoutAimg3.image = rotPhoto2; 
       layoutAimg4.image = rotPhoto1; 
       layoutAimg5.image = rotPhoto1; 
       layoutAimg6.image = rotPhoto2; 
       layoutAimg7.image = rotPhoto2; 
       layoutAimg8.image = rotPhoto1; 


      }else if(myShots == 4){ 

       rotPhoto1 = [self rotateImage:editPhotoData1.size: editPhotoData1]; 
       rotPhoto2 = [self rotateImage:editPhotoData2.size: editPhotoData2]; 
       rotPhoto3 = [self rotateImage:editPhotoData3.size: editPhotoData3]; 
       rotPhoto4 = [self rotateImage:editPhotoData4.size: editPhotoData4]; 

       layoutAimg1.image = rotPhoto1; 
       layoutAimg2.image = rotPhoto2; 
       layoutAimg3.image = rotPhoto3; 
       layoutAimg4.image = rotPhoto4; 
       layoutAimg5.image = rotPhoto1; 
       layoutAimg6.image = rotPhoto2; 
       layoutAimg7.image = rotPhoto3; 
       layoutAimg8.image = rotPhoto4; 


      } 
      break; 

     } 


    UIGraphicsBeginImageContext(photoContainerView.bounds.size); 
    [purikuraContainerView.layer renderInContext:UIGraphicsGetCurrentContext()]; 
    photoFlattened = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 


    NSEnumerator *enumerator = [[photoContainerView subviews] objectEnumerator]; 
    id object; 

    while ((object = [enumerator nextObject])) { 

     [object removeFromSuperview]; 

    } 


    [photoContainerView release]; 

    photoContainerView = nil; 

    if(rotPhoto1 != nil){ 
    [rotPhoto1 release]; 
     rotPhoto1 = nil; 
    } 
    if(rotPhoto2 != nil){ 
    [rotPhoto2 release]; 
    rotPhoto2 = nil; 
    } 
    if(rotPhoto3 != nil){ 
    [rotPhoto3 release]; 
    rotPhoto3 = nil; 
    } 
    if(rotPhoto4 != nil){ 
    [rotPhoto4 release]; 
    rotPhoto4 = nil; 
    } 

    if(rotPhotoSm1 != nil){ 
    [rotPhotoSm1 release]; 
    rotPhotoSm1 = nil; 
    } 
    if(rotPhotoSm2 != nil){ 
    [rotPhotoSm2 release]; 
    rotPhotoSm2 = nil; 
    } 
    if(rotPhotoSm3 != nil){ 
    [rotPhotoSm3 release]; 
    rotPhotoSm3 = nil; 
    } 
    if(rotPhotoSm4 != nil){ 
    [rotPhotoSm4 release]; 
    rotPhotoSm4 = nil; 
    } 

    return photoFlattened; 

} 



//-----------------------------STOP THE UIACTIVITYINDICATORVIEW--------------------- 



-(IBAction)stopSpinner:(id)sender 
{ 

    [self.view setUserInteractionEnabled:YES]; 
    [myIndicator stopAnimating]; 
    [myIndicator release]; 
    myIndicator = nil; 

    if(myPattern == 0){ 
     NSLog(@"SINGLE-SHOT MODE"); 
     controller.isSingleShot = TRUE; 

    }else{ 

     NSLog(@"MULTI-SHOT MODE"); 
     controller.isSingleShot = FALSE; 

    } 

    controller.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; 
    [self presentModalViewController:controller animated:YES]; 

    [controller release]; 

    [allStamps removeAllObjects]; 
    [imageFrames removeAllObjects]; 


    switch (myShots) { 
     case 1: 
      [editPhotoData1 release]; 
      break; 

     case 2: 
      [editPhotoData1 release]; 
      [editPhotoData2 release]; 
      break; 

     case 4: 
      [editPhotoData1 release]; 
      [editPhotoData2 release]; 
      [editPhotoData3 release]; 
      [editPhotoData4 release]; 
      break; 

    } 

     /* This is the edited photo that has been onscreen. Processing is now done so it is okay to release it. The UI should be updated and now have a blank, black background instead of the image. 
*/ 
     editedPhoto.image = nil; 
    [editedPhoto release]; 
    editedPhoto = nil; 


} 
+0

スレッドが実行するメソッドを追加すると便利です。 –

+0

ありがとう、ありがとう。 – RexOnRoids

答えて

15

この質問はココアマルチスレッドでいくつかの良いリソースを持っている:"Where can I find a good tutorial on iPhone/Objective c multithreading?"

また、私は非常にグランドセントラル派遣は、iPhone OS上でまだ利用できないように、ブロック、ディスパッチキューを無視し、しかし (新しい Concurrency Programming Guideを読んでお勧めします iOS 4.0はブロックとGCDを追加したばかりです)。手動で作成したスレッドの代わりに、NSOperationNSOperationQueueのような構造体を使用することが強く求められています。手動で作成されたスレッドの詳細については、Threading Programming Guideを参照してください。

RCに記載されているように、マルチスレッドのCocoaアプリケーションでクラッシュする最大の原因の1つは、共有リソースへの同時アクセスです。 @synchronizedディレクティブはpointed out by Colin Wheelerのように最速ではありませんので、NSLockを使用して共有リソースへのアクセスを保護できます。しかし、どのような種類のロックでも高価になる可能性があります。そのため、これらのリソースへのアクセスには、アプリケーション全体をシングルワイドのNSOperationQueuesに移行しています。パフォーマンスの向上は重要です。

ココアとマルチスレッドのもう1つの問題領域は、ユーザーインターフェイスの更新です。 CocoaのすべてのUIアップデートは、メインスレッドで実行する必要があります。そうしないと、不安定性が発生する可能性があります。計算を実行するバックグラウンドスレッドがある場合は、-performSelectorOnMainThread:withObject:waitUntilDone:メソッド呼び出しでUIを更新するメソッドをラップするようにしてください。

+0

非常に良い...私は特に、UIアップデートがマルチスレッドに問題を引き起こす可能性があると言及している部分に興味があります。私のアプリケーションでは、UIActivityIndi​​catorビューを表示できるように、重要な部分(UIに関する部分を含む)をバックグラウンドスレッドに送信するためです。ときにはクラッシュすることもありますが、時には一定の条件の下では気にしません。これは、アプリケーションが余分なスレッドの影響によるクラッシュ時にiPhone OS自体のコンポーネントに関連する方法に不安定性があるかどうか疑問に思う。私はこれ以上調べなければならないでしょう。ありがとう。 – RexOnRoids

+0

スレッディングの問題によって、非決定的なクラッシュが発生することがよくあります。それが彼らをとても楽しくする理由です。上記のコードでは、バックグラウンドで実行されている-buildPhoto内のコンテキストにレイヤーをレンダリングすることに注意しました。私はそれがスレッドセーフであるかどうかはわかりません。 –

+0

ありがとう!私はそれを調べます。 – RexOnRoids

5

スレッドでの作業はガード/ミューテックスなしに変更可能な共有リソースへのアクセスを許可されたときにおそらく最も一般的な間違いの初心者は、(任意の言語で)作ります。あなたのようなリソースを守る:あなたは、スレッド間で共有されるデータの量を制限したいと思う

 
@synchronized(sharedData) 
{ 
    // modify sharedData safely 
} 

をし、それを共有しなければならない場合、同期によって引き起こされる競合を軽減するために、不変オブジェクトを好みます。

スレッドの管理は、トラブルが発生する可能性のある別の場所です。ここでは、iPhoneでのスレッドの使用に固有のドキュメント参照です。

http://developer.apple.com/iphone/library/documentation/cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html

コードを提供することなく、あなたのアプリに何が間違っているのかは誰でも推測していますが、スレッドの作成と終了を正しく管理していることと、共有リソースアクセスしようとします。

+0

Cool。 RCには別の良いことが書かれています:創造と終結との区別。私は-performSelectorInBackground:withObjectのようなメソッドを使ってスレッドを作成しますが、スレッドの終了時にスレッドが終了すると想定していたため、終了するために何をやっているのか分かりません。私はより多くのドキュメントを読む必要があります。 RCに感謝します。 – RexOnRoids

+0

スレッドは、メソッドが終了すると終了する必要があります。手動で破壊する必要はありません。 –

関連する問題