Szukaj…


Wprowadzenie

W tym temacie opisano, w jaki sposób i kiedy środowisko wykonawcze Swift przydzieli pamięć dla struktur danych aplikacji oraz kiedy ta pamięć zostanie odzyskana. Domyślnie instancjami klasy zaplecza pamięci zarządza się poprzez zliczanie referencji. Struktury są zawsze przekazywane przez kopiowanie. Aby zrezygnować z wbudowanego schematu zarządzania pamięcią, można użyć struktury [ Unmanaged ] [1]. [1]: https://developer.apple.com/reference/swift/unmanaged

Uwagi

Kiedy stosować słowo kluczowe słaby:

weak słowo kluczowe powinno być użyte, jeśli obiekt, do którego istnieje odwołanie, może zostać cofnięty w czasie życia obiektu zawierającego odwołanie.

Kiedy używać słowa kluczowego „unowned”:

unowned należy użyć, jeśli nie oczekuje się, że odwołany obiekt zostanie cofnięty w czasie istnienia obiektu zawierającego odwołanie.

Pułapki

Częstym błędem jest zapominanie o tworzeniu odniesień do obiektów, które muszą żyć po zakończeniu funkcji, takich jak menedżery lokalizacji, menedżery ruchu itp.

Przykład:

class A : CLLocationManagerDelegate
{
    init()
    {
        let locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.startLocationUpdates()
    }
}

Ten przykład nie działa poprawnie, ponieważ menedżer lokalizacji jest zwalniany po powrocie inicjalizatora. Właściwym rozwiązaniem jest utworzenie silnego odwołania jako zmiennej instancji:

class A : CLLocationManagerDelegate
{
    let locationManager:CLLocationManager

    init()
    {
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.startLocationUpdates()
    }
}

Cykle referencyjne i słabe referencje

Cykl odniesienia (lub cykl zachowania ) jest tak nazwany, ponieważ wskazuje cykl na wykresie obiektowym :

zatrzymać cykl

Każda strzałka wskazuje, że jeden obiekt zachowuje drugi (silne odniesienie). Jeśli cykl nie zostanie przerwany, pamięć dla tych obiektów nigdy nie zostanie uwolniona .

Cykl przechowywania jest tworzony, gdy dwa wystąpienia klas odwołują się do siebie:

class A { var b: B? = nil }
class B { var a: A? = nil }

let a = A()
let b = B()

a.b = b  // a retains b
b.a = a  // b retains a -- a reference cycle

Oba wystąpienia będą trwać do momentu zakończenia programu. To jest cykl przechowywania.

Słabe referencje

Aby uniknąć cykli zatrzymania, użyj słowa kluczowego weak lub unowned podczas tworzenia odwołań, aby przerwać cykle zatrzymania.

class B { weak var a: A? = nil }

Słabe lub nieznane referencje nie zwiększą liczby referencji instancji. Te odniesienia nie przyczyniają się do zachowania cykli. Słabe odniesienie staje się nil gdy obiekt, do którego się odwołuje, zostaje zwolniony.

a.b = b  // a retains b
b.a = a  // b holds a weak reference to a -- not a reference cycle

Podczas pracy z zamknięciami możesz także używać weak i unowned na listach przechwytywania .

Ręczne zarządzanie pamięcią

Podczas współpracy z interfejsami API języka C można chcieć wycofać licznik referencji Swift. Można to zrobić w przypadku niezarządzanych obiektów.

Jeśli musisz podać wskaźnik z oznaczeniem typu do funkcji C, użyj metody toOpaque struktury Unmanaged , aby uzyskać surowy wskaźnik, i fromOpaque aby odzyskać oryginalną instancję:

setupDisplayLink() {
  let pointerToSelf: UnsafeRawPointer = Unmanaged.passUnretained(self).toOpaque()
  CVDisplayLinkSetOutputCallback(self.displayLink, self.redraw, pointerToSelf)
}

func redraw(pointerToSelf: UnsafeRawPointer, /* args omitted */) {
  let recoveredSelf = Unmanaged<Self>.fromOpaque(pointerToSelf).takeUnretainedValue()
  recoveredSelf.doRedraw()
}

Pamiętaj, że jeśli używasz passUnretained i odpowiedników, passUnretained zachować wszelkie środki ostrożności, jak w przypadku unowned referencji.

Aby współdziałać ze starszymi interfejsami API Objective-C, można ręcznie wpłynąć na liczbę odwołań do określonego obiektu. W tym celu Unmanaged ma odpowiednie metody retain i release . Niemniej jednak bardziej pożądane jest użycie passRetained i takeRetainedValue , które wykonują zachowanie przed zwróceniem wyniku:

func preferredFilenameExtension(for uti: String) -> String! {
  let result = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension)
  guard result != nil else { return nil }

  return result!.takeRetainedValue() as String
}

Rozwiązania te powinny zawsze być ostatecznością, a interfejsy API dla języków powinny być zawsze preferowane.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow