web-dev-qa-db-fra.com

IBOutlet se bloque avec EXC_BAD_ACCESS même si non nul

Dans un UIViewController (rolePageController), je configure un autre UIViewController (tiroirController) et le passe 2 UIViews à partir de la page de rôle qui fera partie de la configuration de tiroirController. Dès que tiroirController tente d'accéder aux vues IBOutlet à partir de rolePageController, il se bloque avec EXC_BAD_ACCESS (code = EXC_I386_GPFLT). 

Dans le premier VC (rolePageController), voici les IBOutlets:

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

Dans rolePageController.viewDidLoad (), j'appelle le tiroirController.configureDrawer (...):

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
}

Le protocole DrawerViewController est défini comme suit:

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

Voici le code pour la fonction configureDrawer (...):

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


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

Dans le débogueur, vous remarquerez que l'instance tiroirController appelée ne correspond pas à l'instance autonome qui reçoit l'appel. Voici l'adresse de l'instance qui sera appelée:

 enter image description here

Voici l'adresse de l'instance lorsque j'interviens dans l'appel:

 enter image description here

L'adresse de tiroirController avant l'appel n'est pas l'adresse de moi-même lorsque j'interviens dans l'appel. Cela ne devrait jamais arriver.

J'ai créé un projet simplifié qui reproduit l'incident sur https://github.com/ksoftllc/DynamicStackBufferOverflow .

Solution Solution s'est avérée supprimer la clause where du protocole DrawerViewController.

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

J'ai trouvé le code fautif, mais je ne sais pas pourquoi cela causerait les erreurs que je voyais. TiroirController est conforme au protocole DrawerViewController, défini comme suit:

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

Lorsque je supprime la condition Where, elle ne plante plus. 

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

La clause where n’était pas réellement nécessaire au bon fonctionnement du programme, je vais donc continuer sans elle. 

UPDATE J'ai soumis un bogue à Swift.org et reçu une réponse. L'ajout d'une clause where à un protocole n'est pas pris en charge dans Swift 4.2, mais le sera dans Swift 5.0. De plus, @J Doe a publié ci-dessous un moyen d'y parvenir avec une mise à jour de la boîte à outils Xcode.

6
Chuck Krutsinger

dynamic-stack-buffer-overflow n'a rien à voir avec la récursivité. Cela signifie qu'un tampon alloca a été saturé. Vérifiez le code source de asan .

Supposons que la pile soit agencée de sorte que vous ayez un tampon alloca suivi d'un pointeur d'objet - peut-être même l'un des pointeurs d'objet transmis en tant qu'argument.

Supposons que la mémoire tampon alloca soit dépassée. Dans une version asan, cela peut déclencher une erreur dynamic-stack-buffer-overflow. Mais dans une construction non-asan, il écrit simplement sur les octets de ce pointeur d'objet. Supposons qu'il écrit des octets qui forment une adresse qui n'est pas mappée dans la table de pages de votre processus.

Si le programme tente de lire ce pointeur d'objet et de le stocker ailleurs (par exemple, dans une variable d'instance), il doit incrémenter le nombre de références de l'objet. Mais cela signifie qu'il faut déréférencer le pointeur, qui pointe vers une adresse non mappée. Peut-être que cela mène à une faute de protection générale, qui Mach appelle un EXC_I386_GPFLT .

Il serait utile de publier la trace de la pile de l'erreur asan dynamic-stack-buffer-overflow et le désassemblage du code à l'origine de l'erreur.

5
rob mayoff

Cela ressemble vraiment à un bogue du compilateur Swift. J'ai simplifié votre code pour clarification:

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

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

class TestClass: NSObject, TestProtocol {

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

}

foo(TestClass())

Vous pouvez signaler ceci comme un bug. Pour résoudre ce problème, je vous propose de ne pas utiliser l'instruction where ou un objet pass avec le type func foo(_ wow: TestClass {.

3
Timur Bernikovich

Pour résoudre votre problème, exécutez-le sur l’instantané de la chaîne d’outils du coffre de développement. Vous pouvez le télécharger ici:

https://Swift.org/download/

Allez à Instantanés -> Développement du coffre (maître) XCode (donc pas à Swift 5.0) et téléchargez l’instantané à partir du 15 décembre (j’ai eu celui du 30 novembre, mais je suis sûr que le 15 décembre fonctionnera également.)

Après avoir installé la chaîne d’outils, dans XCode, accédez à: File -> Preferences -> Components et sélectionnez la plus récente chaîne d’outils. Il fonctionne maintenant sans aucun crash.

De plus, le Self: UIViewController peut être raccourci en :UIViewcontroller (Ceci ne fonctionne que sur les nouvelles chaînes d'outils):

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