2011-11-16 24 views
10

Tôi rất mới để lập trình và Objective-C và tôi đang cố gắng để tìm ra những gì là sai với ma cua toi. Tôi đã đọc một chút về các khối nhưng tôi không biết làm thế nào bất kỳ những gì tôi đã đọc cho đến nay là có liên quan đến mã của tôi.iOS 5 Twitter Framework & completionHandler khối - "Chụp 'tự' mạnh mẽ trong khối này có khả năng dẫn đến một chu kỳ giữ lại"

Mã của tôi đang sử dụng Khung công tác Twitter 5 của iOS. Tôi sử dụng hầu hết các mẫu mã mà Apple cung cấp vì vậy tôi thực sự không có đầu mối lúc đầu mà tôi đã sử dụng một khối cho trình xử lý hoàn thành.

Bây giờ tôi nhận được hai tin nhắn từ Xcode 4 nói "1. Khối sẽ được giữ lại bởi một đối tượng giữ lại mạnh mẽ bởi các đối tượng bị bắt" và "chụp 'tự' mạnh mẽ trong khối này là khả năng dẫn đến một giữ lại chu kỳ ". Về cơ bản, những gì tôi đã làm là để loại bỏ mã Apple sử dụng trong xử lý hoàn thành của họ (chuyển đổi tuyên bố với TWTweetComposeViewControllerResultCancelled & TWTweetComposeViewControllerResultDone) và sử dụng các câu lệnh if của tôi với [imagePickerController sourceType].

Vì vậy, sendTweet được gọi sau khi một hình ảnh đã được thêm vào tweet.

Tôi hy vọng ai đó có thể giải thích cho tôi lý do tại sao điều này xảy ra và cách tôi có thể giải quyết. Ngoài ra: tôi có thể đặt mã xử lý hoàn thành vào một phương thức thay vì một khối không?

- (void)sendTweet:(UIImage *)image 
{ 
    //adds photo to tweet 
    [tweetViewController addImage:image]; 

    // Create the completion handler block. 
    //Xcode: "1. Block will be retained by an object strongly retained by the captured object" 
    [tweetViewController setCompletionHandler: 
          ^(TWTweetComposeViewControllerResult result) { 
      NSString *alertTitle, 
        *alertMessage, 
        *otherAlertButtonTitle, 
        *alertCancelButtonTitle; 

      if (result == TWTweetComposeViewControllerResultCancelled) 
      { 
       //Xcode: "Capturing 'self' strongly in this block is likely to lead to a retain cycle" 
       if ([imagePickerController sourceType]) 
       { 
        alertTitle = NSLocalizedString(@"TCA_TITLE", nil); 
        alertMessage = NSLocalizedString(@"TCA_MESSAGE", nil); 
        alertCancelButtonTitle = NSLocalizedString(@"NO", nil); 
        otherAlertButtonTitle = NSLocalizedString(@"YES", nil); 

        //user taps YES 
        UIAlertView *alert = [[UIAlertView alloc] 
              initWithTitle:alertTitle 
                message:alertMessage 
                delegate:self // Note: self 
             cancelButtonTitle:alertCancelButtonTitle 
             otherButtonTitles:otherAlertButtonTitle,nil]; 
        alert.tag = 1; 
        [alert show];     
       }    
      } 

Trả lời

21

Vấn đề cơ bản là bạn đang sử dụng self trong một khối. Khối đang được giữ lại bởi đối tượng và chính khối đó cũng giữ lại đối tượng. Vì vậy, bạn có một chu kỳ giữ lại và do đó cả hai có thể sẽ không bao giờ được phát hành bởi vì cả hai đều có một tham chiếu hướng tới chúng. Fortunaly có một giải pháp đơn giản:

Bằng cách sử dụng tham chiếu yếu được gọi là tự chặn sẽ không còn giữ lại đối tượng nữa. Các đối tượng sau đó có thể được phát hành mà sẽ phát hành các khối (thiết lập MyClass để loại thích hợp):

// before your block 
__weak MyObject *weakSelf = self; 

Bên trong khối của bạn, bạn có thể sử dụng weakSelf thay vì self. Lưu ý rằng đây chỉ dành cho iOS 5 sử dụng ARC.

Nhìn vào điều này cũng có một lời giải thích dài rất tốt trên How do I avoid capturing self in blocks when implementing an API? cũng mô tả cách tránh điều này trên iOS 4 và không có ARC.

+0

Cảm ơn sự giúp đỡ của bạn! Vì tôi là một người mới bắt đầu, tôi đã có vấn đề về cách thực hiện điều này. Cuối cùng tôi đã sử dụng __weak UIImagePickerController * weakSelf = imagePickerController; và thay đổi câu lệnh if của tôi thành if ([weakSelf sourceType]). Xcode 4 không hiển thị cho tôi bất kỳ lỗi nào nữa vì vậy tôi cho rằng tôi đã làm đúng. (?) – iMaddin

+0

+1 @Dennis cảm ơn câu trả lời. Cũng vui lòng giải thích khi "__block" được sử dụng. Ví dụ tôi có cú pháp như __block HomeViewController * weakSelf = self; – HDdeveloper

3

Khối của bạn đang giữ lại self vì bạn đang sử dụng self làm đại biểu của UIAlertView. Nếu self cũng giữ lại khối, chúng giữ lại nhau, tạo chu trình giữ lại.

Hãy thử

__block WhateverTypeSelfIs *nonRetainedSelfForBlock = self; 
[tweetViewController setCompletionHandler: 

UIAlertView *alert = [[UIAlertView alloc] 
           initWithTitle:alertTitle 
           message:alertMessage 
           delegate:nonRetainedSelfForBlock 
           cancelButtonTitle:alertCancelButtonTitle 
           otherButtonTitles:otherAlertButtonTitle,nil]; 

Kiểm tra Apple docs, phần Object và Khối Biến. Các đối tượng được tham chiếu trong một khối được giữ lại, trừ khi bạn sử dụng __block.

+0

Cảm ơn bạn đã chỉ ra rằng đã xảy ra sự cố với UIAlertView của tôi trong khối. Kể từ khi tôi đang sử dụng ARC tôi nghĩ rằng Xcode nói với tôi rằng tôi không thể sử dụng __block. Tôi đã sử dụng __weak thay vì TriPhoenix đã đề cập và nó hoạt động rất tốt. Cảm ơn! – iMaddin

+0

+1 @Terry Wilcox, Vui lòng giải thích sự khác biệt giữa "__block" và "__weak". Cảm ơn – HDdeveloper

+0

@HDdeveloper http://stackoverflow.com/questions/11773342/what-the-difference-between-weak-and-block-reference –

0

Có một cách khá đáng ngạc nhiên để loại bỏ cảnh báo.Hãy nhớ bạn, chỉ làm điều này nếu bạn chắc chắn rằng bạn không tạo ra một chu trình giữ lại, hoặc bạn chắc chắn rằng bạn sẽ tự mình phá vỡ nó sau này (bằng cách thiết lập trình xử lý hoàn thành thành không khi bạn làm xong, ví dụ). Nếu bạn có quyền kiểm soát giao diện, hãy đổi tên phương thức của bạn để nó không bắt đầu bằng "set". Trình biên dịch dường như chỉ đưa ra cảnh báo này khi tên phương thức bắt đầu bằng "set".

1

Theo nội dung tôi đã thấy ở nơi khác, “yếu” sẽ không hoạt động đối với mã tuân thủ ARC và thay vào đó bạn phải sử dụng “ _unsafe_unretained”. Đây là những gì tôi đã làm để khắc phục cảnh báo “Tự chụp hình” mạnh mẽ trong khối này có thể dẫn đến cảnh báo lưu giữ ”trong ứng dụng mẫu của Apple" AVPlayerDemo ":

__unsafe_unretained id unself = self; 
mTimeObserver = [mPlayer addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(interval, NSEC_PER_SEC) 
          queue:NULL /* If you pass NULL, the main queue is used. */ 
          usingBlock:^(CMTime time) 
             { 
              /* 'unself' replaced 'self' here: */ 
              [unself syncScrubber]; 
             }]; 
Các vấn đề liên quan