2009-08-31 42 views
8

Tôi vừa giới thiệu đa luồng vào ứng dụng JUST để có được UIActivityIndicatorView ngớ ngẩn để hoạt động. Vâng, các chỉ số hoạt động hoạt động, alright - nhưng bây giờ ứng dụng của tôi bị treo đôi khi, và đôi khi nó không - trong điều kiện khác được kiểm soát ... Tôi cần phải tìm ra điều này nhưng không biết bắt đầu từ đâu ...Lỗi phổ biến đa luồng phổ biến bắt đầu thực hiện trên iPhone

Vì vậy, một số lỗi thường gặp mà người mới bắt đầu thường làm với đa luồng trên iPhone là gì? Vui lòng nêu cụ thể trong câu trả lời của bạn. Cảm ơn vì đã dành thời gian cho tôi.

CẬP NHẬT: Tôi đã thêm nguồn có vấn đề để tham khảo.

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

Có thể hữu ích khi thêm phương thức mà chuỗi của bạn thực thi. –

+0

Ok, tôi sẽ làm điều đó. – RexOnRoids

Trả lời

15

Câu hỏi này có một số tài nguyên tốt về Cocoa đa luồng: "Where can I find a good tutorial on iPhone/Objective c multithreading?"

Tôi cũng khuyên bạn nên đọc mới Concurrency Programming Guide ( Tuy nhiên, bỏ qua các khối và hàng đợi công văn, như Grand Central Dispatch là chưa có sẵn trên iPhone OS iOS 4.0 chỉ thêm khối và GCD), bởi vì nó tạo ra một trường hợp mạnh mẽ để sử dụng các cấu trúc như NSOperationNSOperationQueue như là một thay thế cho các chuỗi được tạo theo cách thủ công. Để biết thông tin về các chuỗi được tạo theo cách thủ công, hãy tham khảo Threading Programming Guide.

Như RC đề cập, nguồn lớn nhất của sự cố với các ứng dụng đa luồng Cocoa là truy cập đồng thời vào một tài nguyên được chia sẻ. Chỉ thị @synchronized không phải là nhanh nhất, như pointed out by Colin Wheeler, vì vậy bạn có thể muốn sử dụng NSLock để bảo vệ quyền truy cập vào tài nguyên được chia sẻ của mình. Tuy nhiên, khóa bất kỳ loại nào có thể tốn kém, đó là lý do tại sao tôi đã di chuyển các ứng dụng của mình sang sử dụng một rộng NSOperationQueues để truy cập vào các tài nguyên này. Các cải tiến hiệu suất đã được đáng kể.

Một vấn đề khác với Cocoa và đa luồng đến từ các cập nhật giao diện người dùng. Tất cả các bản cập nhật giao diện người dùng trong Cocoa phải được thực hiện trên luồng chính hoặc có thể gây mất ổn định. Nếu bạn có chuỗi nền thực hiện phép tính, hãy đảm bảo bọc bất kỳ phương thức nào cập nhật giao diện người dùng trong cuộc gọi phương thức -performSelectorOnMainThread:withObject:waitUntilDone:.

+0

Rất tốt ... Tôi đặc biệt quan tâm đến phần mà bạn đề cập đến cập nhật giao diện người dùng có thể gây ra sự cố với đa luồng. Bởi vì trong ứng dụng của tôi, tôi gửi một đoạn đáng kể công việc (bao gồm một số liên quan đến giao diện người dùng) đến một chuỗi nền để tôi có thể hiển thị chế độ xem UIActivityIndicator. Đôi khi nó bị treo, đôi khi nó không - dưới điều kiện CONSTANT, hãy nhớ bạn. Điều này dẫn người ta tự hỏi liệu sự bất ổn nằm trong cách ứng dụng liên quan đến các thành phần của chính hệ điều hành iPhone tại thời điểm xảy ra sự cố do tác động của chuỗi phụ. Tôi sẽ phải nhìn vào điều này nhiều hơn. Cảm ơn. – RexOnRoids

+0

Vấn đề luồng thường gây ra sự cố không xác định. Đó là những gì làm cho họ rất vui. Tôi đã thông báo trong mã trên của bạn mà bạn làm một số rendering của một lớp vào một bối cảnh trong -buildPhoto đang chạy vào nền. Tôi không chắc đó là một hoạt động an toàn. –

+0

Cảm ơn! Tôi sẽ xem xét điều đó. – RexOnRoids

5

Có lẽ lỗi phổ biến nhất mà người mới bắt đầu thực hiện (bằng bất kỳ ngôn ngữ nào) khi làm việc với chủ đề cho phép truy cập vào tài nguyên được chia sẻ có thể thay đổi mà không cần bảo vệ/mutexes. Bạn bảo vệ tài nguyên như:

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

Bạn sẽ muốn giới hạn số lượng dữ liệu chia sẻ giữa các chủ đề và nếu nó phải được chia sẻ, thích đối tượng bất biến để giảm tranh chấp gây ra bởi việc đồng bộ.

Quản lý chuỗi là một nơi khác mà sự cố có thể phát sinh. Đây là một tài liệu tham khảo cụ thể cho việc sử dụng các chủ đề trong iPhone.

http://developer.apple.com/iphone/library/documentation/cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html. Nếu không cung cấp mã, mọi người có thể đoán được điều gì sai với ứng dụng của bạn, nhưng tôi sẽ bắt đầu bằng cách đảm bảo rằng bạn đang quản lý chính xác quá trình tạo và chấm dứt chuỗi cũng như chú ý đặc biệt đến bất kỳ tài nguyên được chia sẻ nào cố gắng truy cập.

+0

Tuyệt. RC đề cập đến một điều tốt khác: Phân biệt giữa Sáng tạo và Chấm dứt.Tôi sử dụng các phương thức như -performSelectorInBackground: withObject để tạo một luồng, nhưng tôi không biết những gì tôi đang làm để chấm dứt nó như tôi đã giả định rằng ở phần cuối của công việc, luồng sẽ tự kết thúc. Tôi sẽ phải đọc thêm tài liệu. Cảm ơn RC. – RexOnRoids

+0

Chủ đề sẽ chấm dứt khi phương thức kết thúc. Bạn không cần phải tự hủy nó. –

Các vấn đề liên quan