Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EXC_BAD_ACCESS using protocol composition

I want to configure an object with multiple presentables; I've created protocols for them; so that I can combine them into a concrete presentable.

protocol Presentable {

}

protocol TextPresentable: Presentable {
    var text:String { get }
}

protocol ImagePresentable: Presentable {
    var image:String { get }
    var images:[String] { get }
}

The concrete struct:

struct ConcretePresentable: TextPresentable, ImagePresentable {
    var text:String { return "Text" }
    var image:String { return "Image" }
    var images:[String] { return ["A", "B"] }
}

The next thing would be to have a presenter, and I would check if the passed in presenter is actually valid:

typealias TextAndImagePresentable = protocol<TextPresentable, ImagePresentable>

struct ConcretePresenter {
    func configureWithPresentable(presentable: Presentable) {
        guard let textAndImagePresentable = presentable as? TextAndImagePresentable else {
            return
        }

        print(textAndImagePresentable.text)
        print(textAndImagePresentable.image)
        print(textAndImagePresentable.images)
    }
}

To configure:

    let concretePresentable = ConcretePresentable()
    let concretePresenter = ConcretePresenter()

    concretePresenter.configureWithPresentable(concretePresentable)

It goes fine if I run this in a playground, with all of the code in the same place. However, once I put this in a project and split it up into multiple files (ConcretePresenter.swift, ConcretePresentable.swift holding the concrete structs and Presentable.swift which holds the protocols), I get a EXC_BAD_ACCESS.

Why does that happen?

like image 229
MrJre Avatar asked Sep 25 '26 22:09

MrJre


2 Answers

As a disclaimer, I don't necessarily find this answer very satisfying, but it does work.


So, once I noticed that the text & image properties were being returned in each others places (the value for text is being returned by the image property and vice versa), I figured the problem had something to do with what Swift is doing with managing pointers here.

So, out of curiosity, I wanted to add a truly scalar value to the protocols. I added a value property as an Int to the TextPresentable protocol:

protocol Presentable {}

protocol TextPresentable: Presentable {
    var text:String { get }
    var value: Int { get }
}

protocol ImagePresentable: Presentable {
    var image:String { get }
    var images:[String] { get }
}

And then I set up the concrete implementation to return some known value. Here, we're returning 0.

struct ConcretePresentable: TextPresentable, ImagePresentable {
    var text:String { return "SomeText" }
    var value: Int { return 0 }
    var image:String { return "SomeImage" }
    var images:[String] { return ["A", "B"] }
}

After running this code, we still get the same crash, but I notice that value, which really shouldn't have a problem printing 0 is instead printing some very large number: 4331676336. This isn't right at all.

I also changed images from an array to a dictionary to see if the error persists--it does. It seems the crash is related to collections and not specific to arrays.


From here, I tried some other things.

I tried making ConcretePresentable a class rather than a struct.

class ConcretePresentable: TextPresentable, ImagePresentable

That resulted in the same behavior.

I tried making ConcretePresentable conform to the typealias rather than the protocols independently:

struct ConcretePresentable: TextAndImagePresentable

That resulted in the same behavior.

I tried doing both of the aforementioned at once:

class ConcretePresentable: TextAndImagePresentable

Yet still the same behavior.


I did come up with one way to make it work though. Make a protocol that conforms to the two protocols in your typealias and make ConcretePresentable conform to that:

protocol TextAndImagePresentable: TextPresentable, ImagePresentable {}

struct ConcretePresentable: TextAndImagePresentable {
    // ...
}

The problem here is that if you don't explicitly make ConcretePresentable conform to the protocol, it will fail the guard let even if it does conform to TextPresentable and ImagePresentable.

like image 82
nhgrif Avatar answered Sep 28 '26 16:09

nhgrif


I asked about this on Swift Users and filled a bug.

Confirmed to be a bug in the compiler:

https://bugs.swift.org/browse/SR-4477

Fixed by Joe Groff now:

Joe Groff added a comment - 2 hours ago

Merged. Should be fixed in future snapshots.

like image 40
Rudolf Adamkovič Avatar answered Sep 28 '26 17:09

Rudolf Adamkovič



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!