2009-08-31 15 views
8

Uygulamanız için aptalca bir UIActivityIndicatorView elde etmek için uyguladığım SADECE multithreading'i tanıttım. Pekala, etkinlik göstergesi işe yarıyor - ama şimdi uygulamam bazen çöküyor ve bazen de değil - aksi halde kontrol edilen koşullar altında ... Bunu anlamaya ihtiyacım var ama nereye bakmaya başlayacağımı bilmiyorum ...Yeni başlayanlar için yapılan yaygın çoklu okuma hataları, iPhone

Peki, yeni başlayanlar sık ​​sık iPhone'da çok iş parçacıklı hale getirecek bazı yaygın hatalar nelerdir? Lütfen cevaplarınızda belirtin. Zaman ayırdığın için teşekkürler.

UPDATE: Sorunlu kaynağımı referans olarak ekledim. ipler nöbetçiler/muteksler olmadan değişken paylaşılan kaynaklara erişim izin veriyor çalışırken

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

İş parçacığının çalıştırdığı yöntemi eklemek yararlı olabilir. –

+0

Tamam, teşekkürler. – RexOnRoids

cevap

15

Bu soru Kakao çoklu üzerinde bazı iyi kaynaklara sahip: "Where can I find a good tutorial on iPhone/Objective c multithreading?"

de son derece Grand Central Sevk henüz iPhone OS kullanılamaz olarak, bloklar ve sevk sıralarını görmezden Ancak yeni Concurrency Programming Guide ( okuma tavsiye El ile oluşturulan iş parçacıklarına bir alternatif olarak NSOperation ve NSOperationQueue gibi yapıları kullanmak için güçlü bir durum oluşturduğundan, iOS 4.0 yeni eklenen bloklar ve GCD). Elle oluşturulan iş parçacıkları hakkında bilgi için, bkz. Threading Programming Guide.

RC'nin belirttiği gibi, çok iş parçacıklı Kakao uygulamalarına sahip en büyük kilitlenme kaynağı, paylaşılan bir kaynağa eş zamanlı erişimdir. @synchronized yönergesi en hızlı değil, pointed out by Colin Wheeler olarak, bu nedenle paylaşılan kaynaklarınıza erişimi korumak için NSLock kullanmak isteyebilirsiniz. Ancak, herhangi bir türden kilitleme pahalı olabilir, bu yüzden bu kaynaklara erişim için uygulamalarımı tek genişlikli NSOperationQueues kullanmaya geçirdim. Performans iyileştirmeleri önemli olmuştur.

Kakao ve çoklu iş parçacığı içeren başka bir sorun alanı, kullanıcı arabirimi güncelleştirmelerinden gelir. Kakao'daki tüm UI güncellemeleri ana iş parçacığında yapılmalı veya kararsızlık ortaya çıkabilir. Bir hesaplama gerçekleştiren bir arka plan iş parçacığınız varsa, -performSelectorOnMainThread:withObject:waitUntilDone: yöntem çağrısında UI'yi güncelleyen herhangi bir yöntemle sardığınızdan emin olun.

+0

Çok iyi ... Özellikle kullanıcı arabirimi güncelleştirmelerinin çok iş parçacığıyla sorunlara neden olabileceğini düşündüğünüz kısımla ilgileniyorum. Uygulamamda, UIActivityIndicator görünümünü gösterebilmem için önemli bir iş parçasını (UI ile ilişkili olanlar dahil) arka plan iş parçacığına gönderdiğim için. Bazen çöküyor, bazen değil - CONSTANT koşulları altında, aklınızdan çıkarmayın. Bu durum, kararsızlığın, uygulamanın fazladan iş parçacığının etkisinden dolayı çökme anında iPhone işletim sisteminin bileşenlerine ilişkin şekilde olup olmadığını merak eder. Buna daha çok bakmalıyım. Teşekkürler. – RexOnRoids

+0

Threading sorunları genellikle nondeterministic çökmelere neden olur. Onları bu kadar eğlenceli yapan da bu. Yukarıdaki kodunuzda, arka plana çalışan -buildPhoto içindeki bir katmanın bir katmanı oluşturduğunu fark ettim. Bunun sahte bir operasyon olduğundan emin değilim. –

+0

Teşekkürler! Buna bakacağım. – RexOnRoids

5

Muhtemelen en yaygın hata başlayanlar (herhangi bir dilde) olun. Sen gibi kaynakları korumak: Sen evreler arasında paylaşılan veri miktarını sınırlamak için isteyeceksiniz

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

ve paylaşılması gerekir eğer, senkronizasyon kaynaklanan çekişme azaltmak amacıyla iletmenin nesneleri tercih ederim.

Konuları yönetmek, sorunun ortaya çıkabileceği başka bir yerdir. İşte iPhone'da iş parçacığı kullanımına özel bir belge başvurusu.

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

Kodunuz olmadan, uygulamanızın nesi yanlış olduğu konusunda bir tahmin var, ancak iş parçacığının oluşturulmasını ve sonlandırılmasını doğru şekilde yönetdiğinizden emin olmanın yanı sıra, iş parçacığı olan herhangi bir paylaşılan kaynağa özellikle dikkat ederek başladım. erişmeye çalışır.

+0

Serin. RC bir başka güzel şeyden bahseder: Yaratılış ve Sonlandırma arasındaki ayrım.Ben bir iş parçacığı oluşturmak için -performSelectorInBackground: withObject gibi yöntemleri kullanıyorum, ancak işin sonunda iş parçacığının kendisini sonlandıracağını varsaydığım gibi sonlandırmak için ne yaptığımı bilmiyorum. Belgeleri daha fazla okumalıyım. Teşekkürler RC. – RexOnRoids

+0

Yöntem, son noktaya ulaştığında iş parçacığı sonlandırılmalıdır. Manuel olarak yok etmenize gerek yok. –