Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EXC_BAD_ACCESS when trying to call function on prototype

This was an interesting swift problem I ran into. Consider the following class and protocol:

class Person {

}

protocol Parent where Self: Person {
    func speak()
}

class GrandMotherPerson: Person, Parent {
    func speak() {
        print("I am a Grandmother Person")
    }
}

class GrandFatherPerson: Person, Parent {
    func speak() {
        print("I am a Grandfather Person")
    }
}

let gmp = GrandMotherPerson()
let gfp = GrandFatherPerson()

Now when you call

gmp.speak() // Output: I am a Grandmother Person
gfp.speak() // Output: I am a Grandfather Person

But if you cast to Parent

(gmp as Parent).speak() // EXC_BAD_ACCESS when it should say "I am a Grandmother Person"

but if I print(String(describing: gmp))it says it's a __lldb_expr_226.GrandMotherPerson

Why can't swift call speak on the class? If you remove the where Self: Person from the protocol then it works as expected.

like image 837
wyu Avatar asked Jan 23 '18 17:01

wyu


1 Answers

I'm pretty sure this is the same deep issue discussed at length at https://bugs.swift.org/browse/SR-55. Consider that this compiles and runs just fine:

class Person : NSObject {}
@objc protocol Parent where Self: Person {
    func speak()
}
class GrandMotherPerson: Person, Parent {
    func speak() {
        print("I am a Grandmother Person")
    }
}
let gmp = GrandMotherPerson()
let parent = gmp as Parent
parent.speak() // I am a Grandmother Person

But now delete @objc and we get the same issue you're having:

class Person : NSObject {}
protocol Parent where Self: Person {
    func speak()
}
class GrandMotherPerson: Person, Parent {
    func speak() {
        print("I am a Grandmother Person")
    }
}
let gmp = GrandMotherPerson()
let parent = gmp as Parent
parent.speak() // EXC_BAD_ACCESS

So if it's the same issue, the Swift team are very well aware of it, but it runs deep and is difficult to fix. The workaround for now is to use @objc as in my first example.

NOTE I have deliberately separated your last statement into two. That's because there seems to be a further issue with saying (gmp as Parent).speak() - apparently we need an actual variable reference to the existential rather than an implicit temporary.

like image 142
matt Avatar answered Oct 01 '22 13:10

matt