2016-11-03 2 views
1

verursacht habe ich die folgende Funktion:einen Verschluss Passing ein Speicherleck

func attachToComment(_ data: Data, url: URL?, type: MediaType) { 
    self.dismiss(animated: true, completion: nil) 
    let image = UIImage(data: data)! 
    model.attachmentData = data 
    model.attachmentVideoURL = url 
    model.attachmentType = type 

    toolbar.attachImage(image, withType: type) 
} 

Dies ist ein Hinweis auf einen View-Controller übergeben, die modal dargestellt:

containerNavViewController.attachToComment = attachToComment 

ContainerNavViewController ist ein UINavigationController, die hat ein Container View Controller in ihm. Dies zeigt die Kamera und nachdem das Bild aufgenommen wurde, übergibt sie die attachToComment Funktion an den nächsten View-Controller - EditImageViewController. Innerhalb ContainerNavViewController habe ich den folgenden Code:

var attachToComment: ((_ data: Data, _ url: URL?, _ type: MediaType) ->())? 
var model = CameraModel() 
.... 

editImageViewController.attachToComment = self.attachToComment 
editImageViewController.model = model 
self.navigationController?.pushViewController(editImageViewController, animated: false) 

Innen EditImageViewController ich den folgenden Code haben, der die Schließung nennt:

attachToComment(model.imageData, model.videoURL, model.mediaType) 

Diese ruft dann die ursprüngliche Funktion und entlässt den Modal-View-Controller. Diese Ansichten werden jedoch nicht als Deinit bezeichnet und sie "leben" sehr im Hintergrund. Wo ist mein Speicherleck und wie kann ich es verhindern?

+1

Es sieht so aus, als ob Sie einen Retain-Zyklus haben könnten. Self besitzt stark attachToComment (Closures sind erstklassige Objekte), innerhalb der Closure referenzieren Sie self und Variablen, die zu self gehören (Model, Toolbar und die Funktion fill). Als Ergebnis besitzt das Selbst den Verschluss, der Verschluss fängt selbst stark ein und keiner wird sich jemals wieder entziehen, weil sie sich gegenseitig zurückhalten. – Dare

Antwort

2

Wie Dare und Sealos beide vorschlagen, ist das Problem wahrscheinlich ein starker Referenzzyklus. Aber der in der Frage bereitgestellte Code allein reicht nicht aus, um einen solchen Zyklus zu verursachen (denn wenn Sie editImageViewController ablehnen, wenn keine starken Verweise mehr darauf stehen, wird die Schließung aufgehoben und der starke Referenzzyklus wird unterbrochen).

Einige Beobachtungen, aber:

  1. ich konnte Ihren starken Referenzzyklus reproduzieren nur den mitgelieferten Code.

    Aber ich kann einen starken Referenzzyklus induzieren, wenn der View-Controller, der editImageViewController präsentierte, einen starken Verweis darauf hält und verhindert, dass er freigegeben wird. Ist editImageViewController eine lokale Variable oder eine Eigenschaft? Ändern Sie eine Eigenschaft in eine lokale Variable, um das Problem zu beheben. Oder vielleicht behält etwas anderes einen starken Bezug auf editImageViewController (z. B. einen sich wiederholenden Timer oder etwas Ähnliches).

    Mit dem Werkzeug "Debug Memory Graph" (siehe Punkt 3, unten) können Sie feststellen, was sich stark auf editImageViewController bezieht.

  2. Wenn Sie sicherstellen wollte, dass die attachToComment von EditImageViewController keinen starken Hinweis auf die Presenting-View-Controller halten würde, würde ich ersetzen:

    editImageViewController.attachToComment = attachToComment 
    

    mit:

    editImageViewController.attachToComment = { [weak self] data, url, type in 
        self?.attachToComment(data, url: url, type: type) 
    } 
    

    Das ist der einfachste Weg, um sicherzustellen, dass der attachToComment Verschluss in editImageViewController keinen starken Bezug zum präsentierenden View-Controller hält.

  3. Sie könnten das Tool "Debug Memory Graph" in Xcode 8 verwenden, um die Eigentümerschaft des EditImageViewController zu identifizieren. Zum Beispiel habe ich ein Beispiel, wo ich beide:

    • falsch editImageViewController als Eigenschaft des Presenting-View-Controller gespeichert, anstatt sie als lokale Variable (Punkt 1) zu halten; und

    • Das Muster weak im Verschluss (Punkt 2) nicht verwendet.
       

    Als ich das tat, bekam ich eine Erinnerung Graph wie so:

    object graph

    Von diesem Diagramm, muss ich nicht erraten, was die Ursache des Problems ist. Ich kann nicht nur sehen, dass der starke Referenzzyklus existiert, sondern was genau das Problem verursacht. Wenn ich eines der beiden obigen Probleme (oder beide) behebe, verschwindet dieser Zyklus jedoch.

+0

Jeder hier gab ausgezeichnete Antworten! Punkt 1 war die Antwort für mich. Ich hatte einen starken Bezug auf 'containerNavViewController', was eine Eigenschaft war (die wiederum starke Verweise auf die anderen Controller hatte). Wenn Sie diese Einstellung bei der Entlassung auf Null setzen, wird alles korrekt gelöscht. Prost! – Tometoyou

1

Das Problem ist, wenn Sie attachToComment zuweisen, erfasst es den Selbstzeiger des View-Controllers und verhindert die Freigabe.

Es gibt zwei Möglichkeiten, wie Sie dieses Problem lösen können:

  1. Wenn die Ansichten entlassen werden, gleich Null die attachToComment Variable.

EDIT Wegen Robs Korrektur, sondern eine Instanz für den Fang, einen Verschluss mit einem schwachen Selbst Zeiger zurück:

  1. Verwenden Sie ein schwaches Selbst Zeiger in die attachToComment-Variable. Wie so:

    typealias ReturnType = (Data, URL?, MediaType) ->() 
    func attachToComment() -> ReturnType { 
    
        return { [weak self] in 
         self?.dismiss(animated: true, completion: nil) 
         let image = UIImage(data: data)! 
         self?.model.attachmentData = data 
         self?.model.attachmentVideoURL = url 
         self?.model.attachmentType = type 
    
         self?.toolbar.attachImage(image, withType: type) 
        } 
    } 
    
    editImageViewController.attachToComment = containerNavController.attachToComment() 
    

Die Sache ist, wird dies in der Regel mit schwachen Protokollen durchgeführt. Ein Beispiel dafür wäre:

protocol EditImageProtocol: class { 
    func attachToComment(_ data: Data, url: URL?, type: MediaType) 
} 

class ContainerNavController: EditImageProtocol { 
    func attachToComment(_ data: Data, url: URL?, type: MediaType) { 
     self.dismiss(animated: true, completion: nil) 
     let image = UIImage(data: data)! 
     model.attachmentData = data 
     model.attachmentVideoURL = url 
     model.attachmentType = type 

     toolbar.attachImage(image, withType: type) 
    } 
} 

class EditImageViewController { 
    weak var delegate: EditImageProtocol? 

    func someFunction() { 
     delegate?.attachToComment(...) 
    } 
} 
+0

Mein Fehler, Sie sind sicherlich richtig, danke! TIL. – Sealos

+0

@Rob Vielen Dank, jetzt habe ich das vor dem Einreichen getestet! – Sealos