2009-08-31 6 views
8

Acabo de introducir el subprocesamiento múltiple en mi aplicación SOLO para hacer funcionar un UIActivityIndicatorView tonto. Bueno, el indicador de actividad funciona, está bien, pero ahora mi aplicación se cuelga a veces, y otras veces no, bajo condiciones controladas de otra manera ... Necesito resolver esto, pero no sé por dónde empezar a buscar ...Errores de subprocesos comunes que los principiantes cometen en el iPhone

Entonces, ¿cuáles son algunos de los errores más comunes que los principiantes suelen cometer con el multihilo en el iPhone? Por favor sea específico en sus respuestas. Gracias por tu tiempo.

ACTUALIZACIÓN: Agregué mi fuente problemática de referencia.

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

Puede ser útil agregar el método que ejecuta su subproceso. –

+0

Bien, gracias, lo haré. – RexOnRoids

Respuesta

15

Esta pregunta tiene algunos buenos recursos sobre Cocoa multihilo: "Where can I find a good tutorial on iPhone/Objective c multithreading?"

También recomiendo encarecidamente la lectura de la nueva Concurrency Programming Guide ( sin embargo, ignorar los bloques y las colas de despacho, como Grand Central Dispatch aún no está disponible en el iPhone OS iOS 4.0 acaba de agregar bloques y GCD), ya que es una buena razón para usar estructuras como NSOperation y NSOperationQueue como alternativa a los subprocesos creados manualmente. Para obtener información sobre los hilos creados manualmente, consulte el Threading Programming Guide.

Como RC menciona, la fuente más importante de bloqueos con aplicaciones Cocoa multiproceso es el acceso simultáneo a un recurso compartido. La directiva @synchronized no es la más rápida, como pointed out by Colin Wheeler, por lo que es posible que desee utilizar NSLock para proteger el acceso a sus recursos compartidos. Sin embargo, el bloqueo de cualquier tipo puede ser costoso, por lo que he estado migrando mis aplicaciones a usar un solo ancho NSOperationQueues para acceder a estos recursos. Las mejoras de rendimiento han sido significativas.

Otra área problemática con Cocoa y multihilo proviene de las actualizaciones de la interfaz de usuario. Todas las actualizaciones de UI en Cocoa deben realizarse en el hilo principal, o puede producirse inestabilidad. Si tiene un hilo de fondo que realiza un cálculo, asegúrese de ajustar el método que actualice la interfaz de usuario en una llamada a un método -performSelectorOnMainThread:withObject:waitUntilDone:.

+0

Muy bien ... Estoy especialmente interesado en la parte donde mencionas que las actualizaciones de UI pueden causar problemas con el multihilo. Porque en mi aplicación envío una gran cantidad de trabajo (incluidos algunos relacionados con la interfaz de usuario) a un hilo de fondo para que pueda mostrar una vista de UIActivityIndicator. A veces se cuelga, a veces no lo hace, en condiciones CONSTANTES, te preocupes. Esto lleva a uno a preguntarse si la inestabilidad radica en la forma en que la aplicación se relaciona con los componentes del iPhone OS en sí en el momento de la falla debido al impacto del hilo adicional. Tendré que investigar esto más. Gracias. – RexOnRoids

+0

Los problemas de subprocesos a menudo provocan bloqueos no determinísticos. Eso es lo que los hace tan divertidos. Noté en su código anterior que hace una representación de una capa en un contexto dentro de -buildPhoto que se ejecuta en segundo plano. No estoy seguro de que sea una operación segura. –

+0

¡Gracias! Voy a investigar eso. – RexOnRoids

5

Probablemente los principiantes error más común (en cualquier idioma) cuando se trabaja con hilos está permitiendo el acceso a los recursos compartidos mutables y sin protecciones/mutex. A protegerse recursos como:

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

que querrá limitar la cantidad de datos compartidos entre los hilos y si debe ser compartida, prefiere los objetos inmutables con el fin de reducir la contención causado por la sincronización.

Gestionar hilos es otro lugar donde pueden surgir problemas. Aquí hay una referencia de documento específica para el uso de hilos en el iPhone.

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

Sin necesidad de proporcionar el código, nadie puede adivinar cuál es el problema con su aplicación, pero yo comenzaría asegurándome de que está administrando correctamente la creación y terminación del hilo, así como prestando especial atención a los recursos compartidos que intenta acceder

+0

Cool. RC menciona otra cosa buena: Distinción entre Creación y Terminación.Utilizo métodos como -performSelectorInBackground: withObject para crear un hilo, pero no sé lo que estoy haciendo para terminarlo, ya que había asumido que al final del trabajo el hilo simplemente se acabaría. Tendré que leer la documentación más. Gracias RC. – RexOnRoids

+0

El hilo debe terminar cuando el método llega a su fin. No necesita destruirlo manualmente. –

Cuestiones relacionadas