2011-11-16 5 views
10

Jestem bardzo nowy w programowaniu i Objective-C i próbuję ustalić, co jest nie tak z mój kod. Czytałem trochę o blokach, ale nie wiem, czy jakikolwiek z tego, co przeczytałem do tej pory, dotyczy mojego kodu.iOS 5 Twitter Framework i completeHandler block - "Samo przechwytywanie" w tym bloku prawdopodobnie doprowadzi do zatrzymania cyklu "

Mój kod jest oparty na systemie iOS 5 Twitter Framework. Używam większości przykładowego kodu dostarczonego przez firmę Apple, więc na początku nie miałem pojęcia, że ​​korzystam z bloku do obsługi zakończenia.

Teraz mam te dwa komunikaty z Xcode 4 powiedzenie „1. Blok zostaną zachowane przez obiekt silnie zatrzymywane przez przechwyconego obiektu” i "przechwytywanie«ja»zdecydowanie tego bloku może prowadzić do zatrzymaj cykl ".

Zasadniczo, co zrobiłem jest usunięcie Apple kodu używanego w ich obsługi ukończenia (switch z TWTweetComposeViewControllerResultCancelled & TWTweetComposeViewControllerResultDone) i wykorzystywane mój, jeśli sprawozdania z [imagePickerController sourceType].

Tak więc sendTweet zostaje wywołane po dodaniu obrazu do tweeta.

Mam nadzieję, że ktoś może mi wyjaśnić, dlaczego tak się dzieje i jak mogę to rozwiązać. Ponadto: czy mogę umieścić kod obsługi zakończenia w metodzie zamiast bloku?

- (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];     
       }    
      } 

Odpowiedz

21

Podstawowym problemem jest to, że używasz self w jednym bloku. Blok jest zatrzymywany przez obiekt, a sam blok zachowuje również obiekt. Masz więc cykl zatrzymania, a więc oba prawdopodobnie nigdy nie zostaną wypuszczone, ponieważ oba mają odniesienie wskazujące na nie. Na szczęście istnieje proste obejście tego problemu:

Używając tak zwanego słabego odniesienia do siebie, blok nie będzie już zachowywał obiektu. Przedmiotem następnie można później wydany które wyda blok (set MyClass do odpowiedniego rodzaju):

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

Wewnątrz bloku teraz mogą korzystać weakSelf zamiast self. Zauważ, że dotyczy to tylko iOS 5 używającego ARC.

Patrząc na to istnieje również bardzo dobre długie wyjaśnienie na How do I avoid capturing self in blocks when implementing an API?, które opisuje również, jak tego uniknąć na iOS 4 i bez ARC.

+0

Dzięki za pomoc! Ponieważ jestem początkującym początkującym, miałem problem z ustaleniem, jak naprawdę to zaimplementować. W końcu użyłem __weak UIImagePickerController * weakSelf = imagePickerController; i zmień moje instrukcje if na if ([weakSelf sourceType]). Xcode 4 nie pokazuje już żadnych błędów, więc przypuszczam, że postąpiłem słusznie. (?) – iMaddin

+0

+1 @Dennis dzięki za odpowiedź. Proszę również wyjaśnić, kiedy używane jest "__block". Przykład Mam składnię taką jak składnia: __block HomeViewController * weakSelf = self; – HDdeveloper

3

Twój blok zachowuje self, ponieważ używasz self jako delegata UIAlertView. Jeśli self również zachowuje blok, zachowują się nawzajem, co tworzy cykl zatrzymania.

Spróbuj

__block WhateverTypeSelfIs *nonRetainedSelfForBlock = self; 
[tweetViewController setCompletionHandler: 

i

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

Sprawdzić Apple docs sekcja obiektu i zablokować zmiennych. Obiekty przywoływane w bloku są zachowywane, chyba że używa się __block.

+0

Dzięki za wskazanie, że coś było nie tak z moim UIAlertView w bloku. Ponieważ używam ARC, myślę, że Xcode powiedział mi, że nie mogę używać __block. Użyłem __weak zamiast wspomnianego TriPhoenix i działało świetnie. Dzięki! – iMaddin

+0

+1 @ Terry Wilcox, Proszę wyjaśnić różnicę między "__block" i "__weak". Dzięki – HDdeveloper

+0

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

0

Istnieje dość zaskakujący sposób na pozbycie się ostrzeżenia.Pamiętaj, rób to tylko wtedy, gdy jesteś pewien, że nie tworzysz cyklu zatrzymania, lub masz pewność, że sam go złamiesz (ustawiając na przykład obsługę zakończenia, gdy skończysz). Jeśli masz kontrolę nad interfejsem, zmień nazwę metody, aby nie zaczynała się od "zestawu". Wydaje się, że kompilator podaje to ostrzeżenie tylko wtedy, gdy nazwa metody zaczyna się od "set".

1

W zależności od tego, co widziałem gdzie indziej, "słaby" nie działa w przypadku kodu zgodnego z ARC, a zamiast tego należy użyć " _unsafe_unretained". To, co zrobiłem, aby rozwiązać „«ja»Przechwytywanie silnie w tym bloku może doprowadzić do zatrzymania cyklu” ostrzeżenie w przykładowej aplikacji 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]; 
             }];