Jaka jest różnica między referencją słabą a referencją nie posiadaną?

240

Swift ma:

  • Mocne referencje
  • Słabe referencje
  • Nieznane referencje

Czym różni się nieznane odniesienie od słabego odniesienia?

Kiedy bezpiecznie jest używać nieznanego odniesienia?

Czy nieposiadane odniesienia stanowią zagrożenie dla bezpieczeństwa, takie jak zwisające wskaźniki w C / C ++?

Ian Ringrose
źródło
3
Bardzo dobry artykuł na andrewcbancroft.com/2015/05/08/…
Zeeshan
Z mojego doświadczenia wynika unowned, weakże mogę korzystać z klas, które kontrolujemy, z klas Apple, ponieważ nie możemy zagwarantować, że to zrobi
onmyway133,
@NoorAli lub „ownBy” jako odniesienie do „nieposiadanego” często wskazuje na właściciela.
Ian Ringrose,

Odpowiedzi:

361

Zarówno odniesienia, jak weaki unownedodnośniki nie powodują strongzawieszenia określonego obiektu (inaczej nie zwiększają liczby zatrzymań, aby zapobiec cofnięciu przydziału obiektu przez ARC).

Ale dlaczego dwa słowa kluczowe? To rozróżnienie ma związek z faktem, że Optionaltypy są wbudowane w język Swift. Krótka historia na ich temat: opcjonalne typy zapewniają bezpieczeństwo pamięci (działa to pięknie z regułami konstruktora Swift - które są rygorystyczne, aby zapewnić tę korzyść).

weakOdniesienia dopuszcza możliwość, aby stało się nil(to dzieje się automatycznie, gdy odwołuje się obiekt jest zwalniane), w związku z tym rodzajem nieruchomości musi być opcjonalny - więc, jako programista, są zobowiązani do sprawdzenia go przed użyciem (w zasadzie kompilator zmusza Cię, w miarę możliwości, do pisania bezpiecznego kodu).

An unownedzakłada referencyjne, że nigdy nie będzie nilw trakcie jego trwania. Podczas inicjowania należy ustawić nieznane odwołanie - oznacza to, że odwołanie zostanie zdefiniowane jako nie opcjonalny typ, którego można bezpiecznie używać bez kontroli. Jeśli w jakiś sposób obiekt, do którego następuje odwołanie, zostanie zwolniony, aplikacja ulegnie awarii, gdy zostanie użyte odwołanie do właściciela.

Z dokumentów Apple :

Używaj słabego odniesienia, ilekroć jest ważne, aby to odniesienie stało się zerowe w pewnym momencie jego życia. I odwrotnie, użyj nieznanego odwołania, jeśli wiesz, że odniesienie nigdy nie będzie zerowe, jeśli zostanie ustawione podczas inicjalizacji.

W dokumentacji znajdują się przykłady omawiające cykle zatrzymania i sposoby ich przerwania. Wszystkie te przykłady zostały wyodrębnione z dokumentów .

Przykład weaksłowa kluczowego:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    weak var tenant: Person?
}

A teraz, dla niektórych dzieł ASCII (powinieneś zobaczyć dokumenty - mają ładne diagramy):

Person ===(strong)==> Apartment
Person <==(weak)===== Apartment

Te Personi Apartmentprzykład ilustruje sytuację, w której dwie usługi, z których oba są dopuszczone do zera, mają potencjał do wywoływania silnej cyklu odniesienia. Ten scenariusz najlepiej rozwiązać przy słabym odwołaniu. Oba byty mogą istnieć bez ścisłej zależności od drugiego.

Przykład unownedsłowa kluczowego:

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) { self.name = name }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) { self.number = number; self.customer = customer }
}

W tym przykładzie Customermoże CreditCard, ale nie musi , ale CreditCard zawsze będzie związane z Customer. Aby to przedstawić, Customerklasa ma opcjonalną cardwłaściwość, ale CreditCardklasa ma nie opcjonalną (i nie customerposiadaną ) właściwość.

Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard

Te Customeri CreditCardprzykład pokazuje sytuację, w której jedna właściwość, że może być zerowa, a inna właściwość, która nie może być zerowa ma potencjał, by wywołać silną cyklu odniesienia. Ten scenariusz najlepiej rozwiązać przy pomocy nieznanego odniesienia.

Uwaga od Apple:

Słabe odniesienia należy zadeklarować jako zmienne, aby wskazać, że ich wartość może ulec zmianie w czasie wykonywania. Słabego odniesienia nie można zadeklarować jako stałe.

Istnieje również trzeci scenariusz, w którym obie właściwości powinny zawsze mieć wartość, a żadna z właściwości nie powinna nigdy wynosić zero po zakończeniu inicjalizacji.

Istnieją również klasyczne scenariusze cyklu przechowywania, których należy unikać podczas pracy z zamknięciami.

W tym celu zachęcam do odwiedzenia dokumentacji Apple lub przeczytania książki .

Ilea Cristian
źródło
3
Jest to nieco trywialne, ale uważam, że przykład mieszkania i osoby jest nieco mylący, co stanowi również dodatkowe rozwiązanie pozwalające przerwać silny cykl odniesienia. Mieszkanie osoby jest opcjonalne i dlatego może być zerowe, podobnie jak najemca Apartamentu jest opcjonalny i dlatego może być zerowy, więc obie właściwości można zdefiniować jako słabe. ``
Justin Levi Winter
klasa Osoba {let name: String init (name: String) {self.name = name} słaby var apartament: Mieszkanie? } klasa Apartament {let number: Int init (number: Int) {self.number = number} słaby najemca var: Osoba? }
Justin Levi Winter
3
Jaka jest różnica między weak var Person?vs var Person??
Dziekan
4
@JustinLevi, Jeśli zadeklarujesz obie właściwości jako słabe, istnieje możliwość ich zwolnienia. Osoba utrzymuje silne odniesienie do Apartamentu, aby Apartament nie został zwolniony. Gdyby mieszkanie miało takie samo silne odniesienie do Osoby, stworzyliby cykl zatrzymania - który programista może przerwać w czasie wykonywania, jeśli o tym wie, ale w przeciwnym razie jest to wyciek pamięci. To jest całe zamieszanie związane z silnym, słabym i nieznanym: zarządzaniem pamięcią na wyższym poziomie, ponieważ ARC robi dla nas wszystkie brudne rzeczy. Unikanie cykli zatrzymania to nasza praca.
Ilea Cristian
1
Czy jedyną zaletą nieposiadania przez słabości jest to, że nie trzeba rozpakowywać i można użyć stałej? Czy jest jakikolwiek przypadek, w którym nie można użyć słabego i można użyć tylko nie posiadanego?
Alan
29

Pytanie 1 Czym różni się „Nieznany odnośnik” od „Słabej referencji”?

Słabe odniesienie:

Słaba referencja to referencja, która nie utrzymuje silnej kontroli nad instancją, do której się odwołuje, a zatem nie powstrzymuje ARC przed usunięciem instancji, do której się odwołuje. Ponieważ słabe odniesienia mogą mieć „brak wartości”, każde słabe odniesienie należy zadeklarować jako opcjonalne. (Dokumenty Apple)

Nieznany numer referencyjny:

Podobnie jak słabe referencje, nieposiadane referencje nie utrzymują silnej kontroli nad instancją, do której się odnoszą. Jednak w przeciwieństwie do słabego odniesienia, zakłada się, że nie posiadane odniesienie zawsze ma wartość. Z tego powodu nieznane odwołanie jest zawsze definiowane jako typ opcjonalny. (Dokumenty Apple)

Kiedy używać każdego:

Używaj słabego odniesienia, ilekroć jest ważne, aby to odniesienie stało się zerowe w pewnym momencie jego życia. I odwrotnie, użyj nieznanego odwołania, jeśli wiesz, że odniesienie nigdy nie będzie zerowe, jeśli zostanie ustawione podczas inicjalizacji. (Dokumenty Apple)


Q2 Kiedy bezpiecznie jest używać „nieposiadanego odniesienia”?

Jak cytowano powyżej, zakłada się, że nieposiadane odwołanie zawsze ma wartość. Dlatego powinieneś go używać tylko wtedy, gdy masz pewność, że referencja nigdy nie będzie zerowa. Dokumenty Apple ilustrują przypadek użycia nieposiadanych referencji w następującym przykładzie.

Załóżmy, że mamy dwie klasy Customeri CreditCard. Klient może istnieć bez karty kredytowej, ale karta kredytowa nie będzie istnieć bez klienta, tzn. Można założyć, że karta kredytowa zawsze będzie miała klienta. Powinny więc mieć następujący związek:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

Pytanie 3 Są referencjami „nieposiadanymi”, stanowiącymi zagrożenie bezpieczeństwa, takie jak „zwisające wskaźniki” w C / C ++

Nie wydaje mi się

Ponieważ nieznane referencje to po prostu słabe referencje, które z pewnością mają wartość, nie powinno to stanowić żadnego zagrożenia bezpieczeństwa. Jeśli jednak spróbujesz uzyskać dostęp do nieprzyznanego odwołania po wystąpieniu odwołania, do którego odwołuje się odwołanie, wywołasz błąd w czasie wykonywania, a aplikacja ulegnie awarii.

To jedyne ryzyko, jakie z tym widzę.

Link do Apple Docs

Myxtic
źródło
twój przykładowy program z Q2, który jest prosty do zrozumienia o nieposiadanym ... dzięki .. czy możesz dodać ten sam typ przykładu dla słabych i silnych ...
Ranjith Kumar
Doskonały. Dziękuję Ci.
Swifty McSwifterton
Czy możesz podać wspólny przykład nieznajomego lub słabego?
Honey
Rozważ obiekty rodzic i dziecko, jeśli dziecko nie może istnieć bez rodzica, użyj unownedwłaściwości rodzica w klasie potomnej. słaby jest odwrotnie. Ładne wyjaśnienie @myxtic! unownedreferencje to tylko weakreferencje, które mają gwarancję wartości!
Saif,
26

Jeśli jaźń może być zerowa w zamknięciu, użyj [słabego ja] .

Jeśli ja nigdy nie będzie zerowe w zamknięciu, użyj [jaźni nieposiadanej] .

Jeśli zawiesza się, gdy używasz [nieposiadanego ja], wtedy jaźń jest prawdopodobnie zerowa w pewnym momencie tego zamknięcia i prawdopodobnie musisz zamiast tego użyć [słabego ja] .

Sprawdź przykłady używania silnych , słabych i nieposiadanych w zamknięciach:

https://developer.apple.com/library/ios/documentation/swift/conceptual/swift_programming_language/AutomaticReferenceCounting.html

TenaciousJay
źródło
7
Dlaczego po prostu nie używać słabych, nawet jeśli jaźń nigdy nie może być zerowa, bez wyrządzonej krzywdy?
Boon
4
cześć @Boon - to jest rzeczywiście kluczowe pytanie.
Fattie
[słabe ja] => Jeśli użyję zamknięcia wewnątrz viewDidLoad (), jak może selfbyć zero?
Hassan Tareq
@HassanTareq, myślę, że kilka dobrych przykładów znajduje się w wyżej wspomnianym artykule. Sprawdź sekcję „Rozwiązywanie silnych cykli odniesienia dla zamknięć”, szczególnie. Cytat: „Swift wymaga, abyś napisał self.someProperty lub self.someMethod () (zamiast po prostu someProperty lub someMethod ()) za każdym razem, gdy odwołujesz się do członka siebie w zamknięciu. Pomaga to zapamiętać, że możliwe jest uchwycenie siebie poprzez wypadek." Fragment: Apple Inc. „Swift Programming Language (Swift 4)”. iBooks. itunes.apple.com/de/book/the-swift-programming-language-swift-4/… "
Nick Entin
1
@Boon Jeśli zawsze używasz słabego, kompilator wymusi sprawdzenie opcjonalności przed użyciem. Jeśli nie zaznaczysz tej kontroli, pojawi się błąd czasu kompilacji. Nie ma innej szkody.
Vikas Mishra
5

Wyciągi z linku

Kilka punktów końcowych

  • Aby ustalić, czy musisz się martwić silnymi, słabymi lub nieposłusznymi, zapytaj: „Czy mam do czynienia z typami referencyjnymi”. Jeśli pracujesz z Structami lub Enums, ARC nie zarządza pamięcią dla tych typów i nie musisz nawet martwić się określeniem słabych lub nieposiadanych dla tych stałych lub zmiennych.
  • Silne odwołania są odpowiednie w relacjach hierarchicznych, w których rodzic odwołuje się do dziecka, ale nie odwrotnie. W rzeczywistości silne referencje są najczęściej stosownym rodzajem referencji.
  • Gdy dwa wystąpienia są opcjonalnie ze sobą powiązane, upewnij się, że jedno z nich ma słabe odniesienie do drugiego.
  • Gdy dwie instancje są powiązane w taki sposób, że jedna z instancji nie może istnieć bez drugiej, instancja z obowiązkową zależnością musi zawierać nieposiadane odniesienie do drugiej instancji.
Abhinav Singh
źródło
1

Zarówno odniesienia, jak weaki unownedodniesienia nie będą miały wpływu na liczbę odwołań do obiektu. Ale słabe referencje zawsze będą opcjonalne, tzn. Mogą być zerowe, podczas gdy unownedreferencje nigdy nie będą zerowe, więc nigdy nie będą opcjonalne. Korzystając z opcjonalnego odniesienia, zawsze będziesz musiał poradzić sobie z możliwością zerowania obiektu. W przypadku nieznanego odwołania będziesz musiał upewnić się, że obiekt nigdy nie ma wartości zero. Użycie nie posiadanego odniesienia do obiektu zerowego będzie podobne do wymuszonego rozpakowania opcjonalnej wartości zerowej.

To powiedziawszy, że bezpiecznie jest używać nieposiadanego odniesienia, gdy masz pewność, że czas życia obiektu jest dłuższy niż czas odniesienia. Jeśli tak nie jest, lepiej zamiast tego użyć słabego odniesienia.

Jeśli chodzi o trzecią część pytania, nie sądzę, aby niepowiązane odniesienie było podobne do wiszącego wskaźnika. Kiedy mówimy o liczbie referencji, zwykle odnosimy się do dużej liczby referencji obiektu. Podobnie szybkie zachowuje niezliczoną liczbę referencji i słabą liczbę referencji dla obiektu (słabe referencje wskazują na coś, co nazywa się „tabelą boczną”, a nie na sam obiekt). Gdy liczba silnych odniesień osiągnie zero, obiekt zostaje zdezinicjowany, ale nie można go cofnąć, jeśli liczba nieposiadanych odniesień jest większa od zera.

Teraz zwisający wskaźnik jest czymś, co wskazuje na lokalizację pamięci, która została już zwolniona. Ale szybko, ponieważ pamięć może zostać cofnięta tylko tak długo, jak długo istnieje nieprzypisane odniesienie do obiektu, nie może powodować zwisającego wskaźnika.

Istnieje wiele artykułów, które bardziej szczegółowo omawiają szybkie zarządzanie pamięcią. Oto jeden

Deeksha Kaul
źródło
0

Nieznane referencje są rodzajem słabych referencji używanych w przypadku relacji tego samego okresu między dwoma obiektami, gdy obiekt powinien być kiedykolwiek własnością tylko jednego innego obiektu. Jest to sposób na stworzenie niezmiennego wiązania między obiektem a jedną z jego właściwości.

W przykładzie podanym w pośrednim szybkim filmie WWDC osoba posiada kartę kredytową, a karta kredytowa może mieć tylko jednego posiadacza. Na karcie kredytowej osoba nie powinna być opcjonalną własnością, ponieważ nie chcesz, aby karta kredytowa pływała tylko z jednym właścicielem. Możesz przerwać ten cykl, czyniąc właściwość posiadacza kredytu słabym odniesieniem, ale wymaga to również, aby uczynić ją opcjonalną, a także zmienną (w przeciwieństwie do stałej). Nieznane odniesienie w tym przypadku oznacza, że ​​chociaż CreditCard nie posiada udziałów w Osobie, jego życie zależy od niego.

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

    init (holder: Person) {
        self.holder = holder
    }
}
JuJoDi
źródło
link do filmu lub tytułu wwdc?
Osa
-2

Użyj, unownedgdy masz pewność, że selfnigdy nie będziesz nilw miejscu, do którego masz dostęp selfw tym momencie.

Przykład (możesz oczywiście dodać cel bezpośrednio z MyViewController, ale znowu jest to prosty przykład):

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let myButton = MyButton { [unowned self] in
            print("At this point, self can NEVER be nil. You are safe to use unowned.")
            print("This is because myButton can not be referenced without/outside this instance (myViewController)")
        }
    }
}

class MyButton: UIButton {
    var clicked: (() -> ())

    init(clicked: (() -> ())) {
        self.clicked = clicked

        // We use constraints to layout the view. We don't explicitly set the frame.
        super.init(frame: .zero)

        addTarget(self, action: #selector(clicked), for: .touchUpInside)
    }

    @objc private func sendClosure() {
        clicked()
    }
}

Użyj weakkiedy istnieje możliwość, selfmoże być nilw miejscu, które uzyskuje dostęp self.

Przykład:

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        NetworkManager.sharedInstance.receivedData = { [weak self] (data) in
            print("Can you guarentee that self is always available when the network manager received data?")
            print("Nope, you can't. Network manager will be alive, regardless of this particular instance of MyViewController")
            print("You should use weak self here, since you are not sure if this instance is still alive for every")
            print("future callback of network manager")
        }
    }
}

class NetworkManager {

    static let sharedInstance = NetworkManager()

    var receivedData: ((Data) -> ())?

    private func process(_ data: Data) {
        // process the data...

        // ... eventually notify a possible listener.
        receivedData?(data)
    }
}

Wady unowned:

  • Bardziej wydajny niż słaby
  • Możesz (no cóż, jesteś zmuszony) oznaczyć instancję jako niezmienną (już nie od Swift 5.0).
  • Wskazuje czytelnikowi twojego kodu: To wystąpienie ma związek z X i nie może bez niego żyć, ale jeśli X zniknie, ja też zniknę.

Wady weak:

  • Bardziej bezpieczny niż nieposiadany (ponieważ nie może ulec awarii).
  • Może stworzyć relację z X, która działa w obie strony, ale obie mogą żyć bez siebie.

Jeśli nie jesteś pewien, użyj weak. Czekaj , mam na myśli, zapytaj tutaj na StackOverflow, co powinieneś zrobić w swojej sprawie! Używanie słabego przez cały czas, kiedy nie powinieneś, jest po prostu mylące dla ciebie i czytelnika twojego kodu.

J. Doe
źródło