it-swarm.com.de

IBOutlet stürzt mit EXC_BAD_ACCESS ab, auch wenn es nicht gleich Null ist

In einem UIViewController (rolePageController) konfiguriere ich einen anderen UIViewController (drawerController) und übergebe ihm 2 UIViews von der Rollenseite, die Teil der Konfiguration des drawerControllers sein wird. Sobald der drawerController versucht, über den rolePageController auf die IBOutlet-Ansichten zuzugreifen, stürzt er mit EXC_BAD_ACCESS ab (code = EXC_I386_GPFLT). 

Im ersten VC (rolePageController) sind hier die IBOutlets:

@IBOutlet var rolePageDrawerView: UIView!
@IBOutlet var rolePageContentView: UIView!

In rolePageController.viewDidLoad () rufe ich die drawerController.configureDrawer (...) auf:

override func viewDidLoad() {
    super.viewDidLoad()

    //other stuff happens here

    let drawerController = UIStoryboard(name: "StoryboardName", bundle: nil).instantiateViewController(withIdentifier: "drawerController") as! DrawerViewController
    drawerController.configureDrawer(drawerContainerView: self.rolePageDrawerView, overlaidView: self.rolePageContentView)

    //other stuff here
}

Das DrawerViewController-Protokoll ist definiert als:

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

Hier ist der Code für die configureDrawer (...) -Funktion:

private var drawerParentView: UIView!
private var overlaidByDrawerView: UIView!


func configureDrawer(drawerContainerView: UIView, overlaidView: UIView) {
    self.drawerParentView = drawerContainerView
    self.overlaidByDrawerView = overlaidView
}

Im Debugger wurde festgestellt, dass die aufgerufene drawerController-Instanz nicht mit der self-Instanz übereinstimmt, die den Aufruf empfängt. Hier ist die Adresse der Instanz, die aufgerufen wird:

 enter image description here

Hier ist die Adresse der Instanz, wenn ich in den Anruf einsteige:

 enter image description here

Die Adresse von drawerController vor dem Aufruf ist nicht die Adresse von self, wenn ich in den Aufruf einsteige. Das darf niemals passieren.

Ich habe ein vereinfachtes Projekt erstellt, das den Absturz unter https://github.com/ksoftllc/DynamicStackBufferOverflow reproduziert.

Solution Lösung erwies sich als das Entfernen der where-Klausel aus dem DrawerViewController-Protokoll.

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
16

Ich habe den fehlerhaften Code gefunden, aber ich weiß nicht, warum dies die Fehler verursachen würde, die ich sah. Der drawerController entspricht dem DrawerViewController-Protokoll, definiert als:

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

Wenn ich die Where-Bedingung entferne, stürzt sie nicht mehr ab. 

protocol DrawerViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

Die where-Klausel war eigentlich nicht für die korrekte Funktion des Programms notwendig, daher werde ich ohne fortfahren. 

UPDATE Ich habe einen Fehler bei Swift.org eingereicht und eine Antwort erhalten. Das Hinzufügen einer where-Klausel zu einem Protokoll wird in Swift 4.2 nicht unterstützt, wird jedoch in Swift 5.0 unterstützt. Außerdem hat @J Doe einen Weg aufgezeigt, um dies durch ein Update des Xcode-Toolkits zu erreichen.

6

dynamic-stack-buffer-overflow hat nichts mit Rekursion zu tun. Es bedeutet, dass ein alloca-Puffer überschritten wurde. Überprüfen Sie den Laufzeitcode von asan .

Angenommen, der Stapel ist so angelegt, dass Sie einen alloca-Puffer gefolgt von einem Objektzeiger haben - möglicherweise sogar einen der Objektzeiger, die als Argument übergeben werden.

Angenommen, der alloca-Puffer wird überlaufen. In einem asan-Build kann dies einen dynamic-stack-buffer-overflow-Fehler auslösen. In einem nicht asan-Build schreibt er jedoch nur die Bytes dieses Objektzeigers. Angenommen, es schreibt Bytes, die eine Adresse bilden, die nicht in der Seitentabelle Ihres Prozesses abgebildet ist.

Wenn das Programm versucht, diesen Objektzeiger zu lesen und an anderer Stelle zu speichern (beispielsweise in einer Instanzvariablen), muss der Referenzzähler des Objekts erhöht werden. Das bedeutet jedoch, dass der Zeiger dereferenziert wird - und der Zeiger auf eine nicht zugeordnete Adresse zeigt. Vielleicht führt dies zu einer allgemeinen Schutzverletzung, die Mach einen EXC_I386_GPFLT nennt.

Es wäre hilfreich, wenn Sie die Stack-Ablaufverfolgung für den asan dynamic-stack-buffer-overflow-Fehler und die Demontage des Codes, der zu dem Fehler geführt hat, bereitgestellt haben.

5
rob mayoff

Es sieht wirklich wie ein Swift-Compiler-Fehler aus. Ich habe Ihren Code zur Klarstellung vereinfacht:

func foo(_ wow: TestProtocol) {
    wow.foo()
}

protocol TestProtocol where Self: NSObject {
    func foo()
}

class TestClass: NSObject, TestProtocol {

    func foo() {
        print("Much wow")
    }

}

foo(TestClass())

Sie können dies als Fehler melden. Um dieses Problem zu beheben, schlage ich vor, dass Sie keine where-Anweisung oder Objekt mit dem Typ func foo(_ wow: TestClass { verwenden.

3

Um Ihr Problem zu beheben, führen Sie es im Snapshot der Toolchain der Entwicklungs-Trunk aus. Sie können es hier herunterladen:

https://Swift.org/download/

Gehen Sie zu Snapshots -> Trunk Development (Master) XCode (nicht Swift 5.0) und laden Sie den Snapshot ab dem 15. Dezember herunter (ich habe den vom 30. November erhalten, aber ich bin mir sicher, dass auch der 15. Dezember funktionieren wird.)

Nachdem Sie die Toolchain installiert haben, gehen Sie in XCode zu: File -> Preferences -> Components und wählen Sie die neueste Toolchain aus. Es läuft jetzt ohne Absturz.

Außerdem kann das where Self: UIViewController zu :UIViewcontroller verkürzt werden (Dies funktioniert nur bei den neuesten Toolchains):

protocol DrawerViewController: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
0
J. Doe