2016-08-21 55 views
7

Zrobiłem trochę badań nad stackoverflow i dokumentacji Apple o ARC i Weak/Unowned siebie (Shall we always use [unowned self] inside closure in Swift). Mam podstawowe pojęcie o silnym cyklu odniesienia i o tym, że nie jest ono dobre, ponieważ powoduje wycieki pamięci. Jednak staram się zrozumieć, kiedy używać słabego/niezasilanego siebie w zamknięciach. Zamiast wchodzenia w "teorię", myślę, że naprawdę pomogłoby, gdyby ktoś mógł wyjaśnić je w kategoriach trzech ostatnich spraw, które mam. Moje pytania toSłabe samo w zamknięciach i przykładach konsekwencji

  1. Czy można umieścić słaby siebie w każdym z nich (myślę, że dla przypadku dwóch nie ma potrzeby, bo widziałem gdzieś, że UIView nie jest związany z siebie ?. Jednak to, co jeśli kładę słabe ja, czy jest coś, co może wywołać ból głowy?

  2. Powiedz, jeśli odpowiedź brzmi "Nie", nie możesz umieścić słabego ja w każdym z trzech przypadków, co by się stało, gdybym to zrobiła (przykładowa reakcja byłaby bardzo doceniana. ..Na przykład, program ulegnie awarii, gdy ten VC ...

  3. Oto, w jaki sposób mam zamiar użyć weakSelf Poza zamknięciem, kładę słabe var weakSelf = self Następnie wymienić wszystkie self w zamknięciu z weakSelf? Czy to jest w porządku?

    Case 1: 
    FIRAuth.auth()?.signInWithCredential(credential, completion: { (user: FIRUser?, error: NSError?) in 
        self.activityIndicatorEnd() 
        self.performSegueWithIdentifier(SEGUE_DISCOVER_VC, sender: self) 
    }) 
    
    Case 2: 
    UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.1, animations: { 
        self.messageLbl.alpha = 0.5 
    }) 
    
    Case 3: 
    //checkUserLoggedIn sends a request to firebase and waits for a response to see if the user is still authorised 
    checkUserLoggedIn { (success) in 
        if success == false { 
         // We should go back to login VC automatically 
    
        } else {   
         self.discoverTableView.delegate = self 
         self.discoverTableView.dataSource = self 
    
         // Create dropdown menu 
         let menuView = BTNavigationDropdownMenu(navigationController: self.navigationController, title: self.dropDownItems.first!, items: self.dropDownItems) 
    
         menuView.didSelectItemAtIndexHandler = {[weak self] (indexPath: Int) ->() in 
          if indexPath == 0 { 
           self?.mode = .Closest 
           self?.sortByDistance() 
    
          } else if indexPath == 1 { 
           self?.mode = .Popular 
           self?.sortByPopularity() 
    
          } else if indexPath == 2 { 
           self?.mode = .MyPosts 
           self?.loadMyPosts() 
    
          } else { 
           print("Shouldnt get here saoihasiof") 
          } 
         } 
    
        // Xib 
         let nib = UINib(nibName: "TableSectionHeader", bundle: nil) 
         self.xibRef = nib.instantiateWithOwner(self, options: nil)[0] as? TableSectionHeader 
         self.discoverTableView.registerNib(nib, forHeaderFooterViewReuseIdentifier: "TableSectionHeader") 
    
         // Set location Manager data 
         self.locationManager.delegate = self 
         self.locationManager.desiredAccuracy = kCLLocationAccuracyBest 
    
         // Check location service status 
         if self.locationAuthStatus == CLAuthorizationStatus.AuthorizedWhenInUse { 
          // Already authorised 
          self.displayMessage.hidden = false 
    
         } else if self.locationAuthStatus == CLAuthorizationStatus.NotDetermined { 
          // Have not asked for location service before 
          let storyboard = UIStoryboard(name: "Main", bundle: nil) 
          let vc = storyboard.instantiateViewControllerWithIdentifier("LocationVC") as! LocationVC 
          vc.locationVCDelegate = self 
          self.presentViewController(vc, animated: true, completion: nil) 
    
         } else { 
          let alertController = UIAlertController(title: "Enable Location", message: "location is required to load nearby posts", preferredStyle: .Alert) 
          let cancelAction = UIAlertAction(title: "Cancel", style: .Default, handler: nil) 
          let settingsAction = UIAlertAction(title: "Settings", style: .Default, handler: { (action: UIAlertAction) in 
           let settingsUrl = NSURL(string: UIApplicationOpenSettingsURLString) 
           if let url = settingsUrl { 
            UIApplication.sharedApplication().openURL(url) 
           } 
          }) 
          alertController.addAction(settingsAction) 
          alertController.addAction(cancelAction) 
          self.presentViewController(alertController, animated: true, completion: nil) 
    
          self.displayMessage.hidden = false 
          self.displayMessage.text = "Could not determine your location to find nearby posts. Please enable location Service from settings" 
         } 
    
         // Styling 
         self.refreshBtn.tintColor = COLOR_NAVIGATION_BUTTONS 
         self.discoverTableView.backgroundColor = COLOR_DISCOVERVC_TABLEVIEW_BACKGROUND 
    
         // Allow navigation bar to hide when scrolling down 
         self.hidingNavBarManager = HidingNavigationBarManager(viewController: self, scrollView: self.discoverTableView) 
    
         // Allow location to start updating as soon as we have permission 
         self.locationManager.startUpdatingLocation() 
        } 
    } 
    

--Update-- Większość mojego kodu wygląda przypadek 3, w którym wszystko jest owinięty wewnątrz zamknięcia, które albo sprawdzić, czy nie ma połączenia z Internetem, zanim którykolwiek z działaniem jest dokonany. Więc mogę mieć słabe jaźń wszędzie?

--update 2--

Case 4: 
// The haveInternetConnectivity function checks to see if we can reach google within 20 seconds and return true if we can 
haveInternetConnectivity { (success) in 
    if success == false { 
     self.dismissViewControllerAnimated() 
    } else {   
     self.label.text = "You are logged in" 
     self.performSegueWithIdentifier("GoToNextVC") 
    } 
} 

Pytanie o wypadku 4. mam rację powiedzieć, że mimo to zamknięcie nie ma słabą/pozostawiony siebie, to nigdy tworzyć silne odniesienia (i pamięci wyciek), ponieważ nawet jeśli VC zostanie odwołany przed wykonaniem bloku zakończenia, Xcode spróbuje uruchomić kod wewnątrz bloku zakończenia, gdy potwierdzimy status Internetu i po prostu nie zrobimy nic (bez awarii), ponieważ self już nie istnieje. A kiedy kod dotrze do ostatniej linii wewnątrz zamknięcia, silne odniesienie do siebie zostanie zniszczone, a więc zwolnić VC?

więc oddanie [słabą Jaźni] w tym przypadku byłoby to po prostu oznacza, że ​​Xcode ignoruje te linie (jak sprzeciwiać, aby spróbować go uruchomić i nic się nie dzieje), co oznaczałoby lepsze praktyki, ale żadnych problemów na ręku albo sposób

Odpowiedz

13

Pytanie nie powinno brzmieć "czy mogę użyć słabego odniesienia", ale raczej "czy powinienem użyć słabego odniesienia". Używasz słabych odniesień, aby uniknąć silnych cykli odniesienia lub utrzymać zamknięcie z zawieszania się na czymś po usunięciu. Ale nie dodawaj słabych referencji, ponieważ możesz może.

  1. W przypadku 1, prawdopodobnie chcesz korzystać [weak self]. Czemu? Ponieważ jeśli kontroler widoku został zwolniony podczas autoryzacji, czy na pewno chcesz zachować odwołanie do kontrolera widoku, który został zwolniony? Prawdopodobnie nie w tym przypadku.

  2. W przypadku 2 teoretycznie można użyć [weak self] w bloku animation, ale dlaczego? Nie ma powodu do tego.Słabym odniesieniem jest coś, co robisz z programami obsługi zakończenia i/lub zmiennymi zamknięcia, ale dla bloku animacji nie oferuje on żadnego narzędzia, więc nie zrobiłbym tego w tym miejscu. Używanie weak tutaj sugeruje nieporozumienie związane z semantyką pamięci.

  3. W przypadku 3 występują dwa osobne problemy.

    • W didSelectItemAtIndexHandler, że prawdopodobnie należy użyć [unowned self] ponieważ własny zamknięcie obiektu wynosi odnosząc się do siebie.

      To może być kwestia sporna, ponieważ nie widzę, żebyś faktycznie używał tego BTNavigationDropdownMenu (być może ten inicjator dodaje się do kontrolera nawigacyjnego, ale to nie jest dobrze zaprojektowany inicjator, jeśli tak, to IMHO).

      Ale jako ogólna koncepcja, gdy obiekt ma zamknięcie obsługi, które można wywołać tylko wtedy, gdy obiekt nadal znajduje się w pobliżu, ale nie powinien sam powodować zatrzymania obiektu, należy użyć [unowned self].

    • W szerszym zamknięciu checkUserLoggedIn pojawia się pytanie, czy jest to program obsługi zakończenia. Jeśli tak, prawdopodobnie powinieneś użyć [weak self], ponieważ może to zostać zainicjowane i być uruchomione przed upływem czasu, gdy self zostanie odrzucone, a Ty nie chcesz, aby checkUserLoggedIn zachował odwołanie do kontrolera widoku, który został zwolniony. Ale nie chciałbyś używać [unowned self], ponieważ zostawiłoby to zwisające wskaźniki, gdyby zostały zwolnione przed zakończeniem zamknięcia.

      Tak na marginesie, to kontemplować:

      weak var weakSelf = self 
      

      czyli nieco unswifty. Można użyć wzoru [weak self] na początku zamknięcia checkUserLoggedIn. Jeśli masz przykład, w którym masz ochotę użyć weak var weakSelf = ..., powinieneś edytować swoje pytanie, w tym przykład miejsca, w którym chcesz użyć tego wzoru. Ale to nie jest jedna z tych spraw.

+0

Proszę wyjaśnić, co rozumiecie przez "nie oferuje użyteczności". Czy można uczciwie powiedzieć, że każde zamknięcie, które obejmuje każdy rodzaj czekania z Internetu, zawsze musi być słabe lub niezarejestrowane? I czy to prawda, że ​​prawie we wszystkich przypadkach byłaby to słaba jaźń? – user172902

+1

Słabe referencje w zamknięciach 'animation' nie oferujących użyteczności: Zamknięcie' animation' dostarczone do 'animateWithDuration' nie jest programem obsługi zakończenia, który jest uruchamiany później (chociaż istnieje oddzielne zamknięcie' complete', ale jest to oddzielna kula wosku całkowicie). Nazywa się to natychmiast, a animacja jest inicjowana, ale zamknięcie nie zachowuje żadnych silnych odniesień do "ja" na czas trwania animacji. – Rob

+1

Zamknięcia sieci re są zawsze słabe/niezarejestrowane: Nie, z pewnością tak nie jest.Na przykład, możesz zrobić coś w tym zamknięciu, które powinno być uruchomione (np. Zaktualizuj lokalną bazę danych dotyczącą zakończenia żądania sieciowego, zapisz pobrany obraz do pamięci podręcznej, itp.). Używaj tylko słabych _ jeśli nie potrzebujesz/chcesz, aby kod znajdował się w zamknięciu, aby zachować odniesienie do obiektu_ (np. Po prostu aktualizujesz interfejs użytkownika, ale nie aktualizujesz pamięci podręcznych lub baz danych lub struktur modelu). Nie używaj bezmyślnie słaby ": patrz na to, co jest w zamknięciu i zdecyduj, czy powinno być (lub musi być) słabe, czy nie. – Rob

-1

słabe odniesienia

Słaby odniesienia jest punktem odniesienia, który nie prowadzi mocną pozycję na przykład dotyczy, a więc nie zatrzymać łukowego rozstrzygających referencyjna instancja. Takie zachowanie uniemożliwia odniesienie do stania się częścią silnego cyklu odniesienia. Wskazujesz słabe odniesienie, umieszczając słabe słowo kluczowe przed deklaracją właściwości lub zmiennej.

Dla większej przejrzystości słabych, przeczytaj the doc here.

+0

Wygląda na to, że OP rzeczywiście zna słabe odniesienie. Ale on chce wiedzieć o słabym ja, którego nie traktujesz w swojej odpowiedzi. To nie odpowiada na pytanie. –